Skip to content

Commit 3dde460

Browse files
committed
Merge branch 'main' into ec2/sync3
2 parents 46c4ed2 + f8d6049 commit 3dde460

File tree

13 files changed

+193
-297
lines changed

13 files changed

+193
-297
lines changed

Cargo.toml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ native = ["tonic/channel", "tonic/gzip", "tonic/tls-webpki-roots", "tokio/macros
4141
sqlite-db = ["dep:zcash_client_sqlite"]
4242
console_error_panic_hook = ["dep:console_error_panic_hook"]
4343
no-bundler = ["wasm-bindgen-rayon?/no-bundler", "wasm_thread/no-bundler"]
44-
sync2 = []
45-
sync3 = []
4644

4745
[dependencies]
4846
## Web dependencies
@@ -62,12 +60,13 @@ tokio_with_wasm = { version = "0.7.1", features = ["rt", "rt-multi-thread", "syn
6260

6361
## Zcash dependencies
6462

65-
zcash_keys = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4", features = ["transparent-inputs", "orchard", "sapling", "unstable"] }
66-
zcash_client_backend = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4", default-features = false, features = ["transparent-inputs", "sync", "lightwalletd-tonic", "wasm-bindgen", "orchard"] }
67-
zcash_client_memory = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4", features = ["orchard", "transparent-inputs"] }
68-
zcash_primitives = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4" }
69-
zcash_address = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4" }
70-
zcash_proofs = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4", default-features = false, features = ["bundled-prover"] }
63+
zcash_keys = { git = "https://github.com/ChainSafe/librustzcash", rev = "e9f82490ac370f221a2650ae1b2563c8ccadf4c8", features = ["transparent-inputs", "orchard", "sapling", "unstable"] }
64+
zcash_client_backend = { git = "https://github.com/ChainSafe/librustzcash", rev = "e9f82490ac370f221a2650ae1b2563c8ccadf4c8", default-features = false, features = ["transparent-inputs", "sync", "lightwalletd-tonic", "wasm-bindgen", "orchard"] }
65+
zcash_client_memory = { git = "https://github.com/ChainSafe/librustzcash", rev = "e9f82490ac370f221a2650ae1b2563c8ccadf4c8", features = ["orchard", "transparent-inputs"] }
66+
zcash_primitives = { git = "https://github.com/ChainSafe/librustzcash", rev = "e9f82490ac370f221a2650ae1b2563c8ccadf4c8" }
67+
zcash_address = { git = "https://github.com/ChainSafe/librustzcash", rev = "e9f82490ac370f221a2650ae1b2563c8ccadf4c8" }
68+
zcash_proofs = { git = "https://github.com/ChainSafe/librustzcash", rev = "e9f82490ac370f221a2650ae1b2563c8ccadf4c8", default-features = false, features = ["bundled-prover"] }
69+
7170

7271
## gRPC Web dependencies
7372
prost = { version = "0.12", default-features = false }
@@ -78,7 +77,7 @@ tonic = { version = "0.12", default-features = false, features = [
7877

7978
# Used in Native tests
8079
tokio = { version = "1.0" }
81-
zcash_client_sqlite = { git = "https://github.com/ChainSafe/librustzcash", rev = "b6d32dd9a57165fb1508e9c1c8ab1a3aba09c7f4", default-features = false, features = ["unstable", "orchard"], optional = true }
80+
zcash_client_sqlite = { git = "https://github.com/ChainSafe/librustzcash", rev = "9673cc2859e8a2528d1efd3c74795363f87ddf8f", default-features = false, features = ["unstable", "orchard"], optional = true }
8281

8382
getrandom = { version = "0.2", features = ["js"] }
8483
thiserror = "1.0.63"

justfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@ default:
22
just --list
33

44
build *features:
5-
wasm-pack build --no-opt -t web --scope webzjs --release --out-dir ./packages/webz-core --no-default-features --features="wasm wasm-parallel sync2 {{features}}" -Z build-std="panic_abort,std"
5+
wasm-pack build --no-opt -t web --scope webzjs --release --out-dir ./packages/webz-core --no-default-features --features="wasm wasm-parallel {{features}}" -Z build-std="panic_abort,std"
66

77
# All Wasm Tests
88
test-web *features:
99
WASM_BINDGEN_TEST_TIMEOUT=99999 wasm-pack test --release --firefox --no-default-features --features "wasm no-bundler {{features}}" -Z build-std="panic_abort,std"
1010

11-
# sync message board in the web: addigional args: sync2
11+
# sync message board in the web: addigional args:
1212
test-message-board-web *features:
1313
WASM_BINDGEN_TEST_TIMEOUT=99999 wasm-pack test --release --chrome --no-default-features --features "wasm no-bundler {{features}}" -Z build-std="panic_abort,std" --test message-board-sync
1414

15-
# simple example in the web: additional args: sync2
15+
# simple example in the web: additional args:
1616
test-simple-web *features:
1717
WASM_BINDGEN_TEST_TIMEOUT=99999 wasm-pack test --release --chrome --no-default-features --features "wasm no-bundler {{features}}" -Z build-std="panic_abort,std" --test simple-sync-and-send
1818

1919

20-
# simple example: additional args: sync2, sqlite-db
20+
# simple example: additional args:, sqlite-db
2121
example-simple *features:
2222
RUST_LOG="info,zcash_client_backend::sync=debug" cargo run -r --example simple-sync --features "native {{features}}"
2323

24-
# sync the message board: additional args: sync2, sqlite-db
24+
# sync the message board: additional args:, sqlite-db
2525
example-message-board *features:
2626
RUST_LOG=info,zcash_client_backend::sync=debug cargo run -r --example message-board-sync --features "native {{features}}"
2727

packages/demo-wallet/src/App/Actions.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import initWasm, { initThreadPool, WebWallet } from "@webzjs/webz-core";
22

33
import { State, Action } from "./App";
4-
import { MAINNET_LIGHTWALLETD_PROXY } from "./constants";
4+
import { MAINNET_LIGHTWALLETD_PROXY } from "./Constants";
55

66
export async function init(dispatch: React.Dispatch<Action>) {
77
await initWasm();
@@ -13,8 +13,8 @@ export async function init(dispatch: React.Dispatch<Action>) {
1313
}
1414

1515
export async function addNewAccount(state: State, dispatch: React.Dispatch<Action>, seedPhrase: string, birthdayHeight: number) {
16-
await state.webWallet?.create_account(seedPhrase, 0, birthdayHeight);
17-
dispatch({ type: "append-account-seed", payload: seedPhrase });
16+
let account_id = await state.webWallet?.create_account(seedPhrase, 0, birthdayHeight) || 0;
17+
dispatch({ type: "add-account-seed", payload: [account_id, seedPhrase] });
1818
await syncStateWithWallet(state, dispatch);
1919
}
2020

@@ -60,6 +60,13 @@ export async function triggerTransfer(
6060
throw new Error("No active account");
6161
}
6262

63-
await state.webWallet?.transfer(state.accountSeeds[state.activeAccount], state.activeAccount, toAddress, amount);
64-
await syncStateWithWallet(state, dispatch);
63+
let activeAccountSeedPhrase = state.accountSeeds.get(state.activeAccount) || "";
64+
65+
let proposal = await state.webWallet?.propose_transfer(state.activeAccount, toAddress, amount);
66+
console.log(JSON.stringify(proposal.describe(), null, 2));
67+
68+
let txids = await state.webWallet.create_proposed_transactions(proposal, activeAccountSeedPhrase);
69+
console.log(JSON.stringify(txids, null, 2));
70+
71+
await state.webWallet.send_authorized_transactions(txids);
6572
}

packages/demo-wallet/src/App/App.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ export type State = {
2323
activeAccount?: number;
2424
summary?: WalletSummary;
2525
chainHeight?: bigint;
26-
accountSeeds: string[];
26+
accountSeeds: Map<number, string>;
2727
};
2828

2929
const initialState: State = {
3030
activeAccount: undefined,
3131
summary: undefined,
3232
chainHeight: undefined,
33-
accountSeeds: [],
33+
accountSeeds: new Map<number, string>(),
3434
};
3535

3636
export type Action =
3737
| { type: "set-active-account"; payload: number }
38-
| { type: "append-account-seed"; payload: string }
38+
| { type: "add-account-seed"; payload: [number, string] }
3939
| { type: "set-web-wallet"; payload: WebWallet }
4040
| { type: "set-summary"; payload: WalletSummary }
4141
| { type: "set-chain-height"; payload: bigint };
@@ -45,8 +45,8 @@ const reducer = (state: State, action: Action): State => {
4545
case "set-active-account": {
4646
return { ...state, activeAccount: action.payload };
4747
}
48-
case "append-account-seed": {
49-
return { ...state, accountSeeds: [...state.accountSeeds, action.payload] };
48+
case "add-account-seed": {
49+
return { ...state, accountSeeds: state.accountSeeds.set(action.payload[0], action.payload[1]) };
5050
}
5151
case "set-web-wallet": {
5252
return { ...state, webWallet: action.payload };

packages/demo-wallet/src/App/components/ImportAccount.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function ImportAccount() {
1818
await addNewAccount(state, dispatch, seedPhrase, birthdayHeight);
1919
toast.success("Account imported successfully", {
2020
position: "top-center",
21+
autoClose: 2000,
2122
});
2223
setBirthdayHeight(0);
2324
setSeedPhrase("");

src/bindgen/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod proposal;
12
pub mod wallet;

src/bindgen/proposal.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use wasm_bindgen::prelude::*;
2+
3+
use super::wallet::NoteRef;
4+
use zcash_primitives::transaction::fees::zip317::FeeRule;
5+
6+
/// A handler to an immutable proposal. This can be passed to `create_proposed_transactions` to prove/authorize the transactions
7+
/// before they are sent to the network.
8+
///
9+
/// The proposal can be reviewed by calling `describe` which will return a JSON object with the details of the proposal.
10+
#[wasm_bindgen]
11+
pub struct Proposal {
12+
inner: zcash_client_backend::proposal::Proposal<FeeRule, NoteRef>,
13+
}
14+
15+
impl From<zcash_client_backend::proposal::Proposal<FeeRule, NoteRef>> for Proposal {
16+
fn from(inner: zcash_client_backend::proposal::Proposal<FeeRule, NoteRef>) -> Self {
17+
Self { inner }
18+
}
19+
}
20+
21+
impl From<Proposal> for zcash_client_backend::proposal::Proposal<FeeRule, NoteRef> {
22+
fn from(proposal: Proposal) -> Self {
23+
proposal.inner
24+
}
25+
}
26+
27+
#[wasm_bindgen]
28+
impl Proposal {
29+
pub fn describe(&self) -> JsValue {
30+
serde_wasm_bindgen::to_value(&self.inner).unwrap()
31+
}
32+
}

src/bindgen/wallet.rs

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
use std::num::NonZeroU32;
22

3+
use nonempty::NonEmpty;
34
use serde::{Deserialize, Serialize};
45
use wasm_bindgen::prelude::*;
56

67
use tonic_web_wasm_client::Client;
78

89
use crate::error::Error;
9-
use crate::{BlockRange, MemoryWallet, Wallet, PRUNING_DEPTH};
10+
use crate::wallet::usk_from_seed_str;
11+
use crate::{bindgen::proposal::Proposal, BlockRange, Wallet, PRUNING_DEPTH};
1012
use wasm_thread as thread;
1113
use zcash_address::ZcashAddress;
14+
use zcash_client_backend::data_api::{InputSource, WalletRead};
1215
use zcash_client_backend::proto::service::{
1316
compact_tx_streamer_client::CompactTxStreamerClient, ChainSpec,
1417
};
1518
use zcash_client_memory::MemoryWalletDb;
1619
use zcash_keys::keys::UnifiedFullViewingKey;
17-
use zcash_primitives::consensus::{self, BlockHeight};
20+
use zcash_primitives::consensus;
21+
use zcash_primitives::transaction::TxId;
22+
23+
pub type MemoryWallet<T> = Wallet<MemoryWalletDb<consensus::Network>, T>;
24+
pub type AccountId =
25+
<MemoryWalletDb<zcash_primitives::consensus::Network> as WalletRead>::AccountId;
26+
pub type NoteRef = <MemoryWalletDb<zcash_primitives::consensus::Network> as InputSource>::NoteRef;
1827

1928
/// # A Zcash wallet
2029
///
@@ -87,61 +96,44 @@ impl WebWallet {
8796
///
8897
/// # Arguments
8998
/// seed_phrase - mnemonic phrase to initialise the wallet
90-
/// account_index - The HD derivation index to use. Can be any integer
99+
/// account_hd_index - The HD derivation index to use. Can be any integer
91100
/// birthday_height - The block height at which the account was created, optionally None and the current height is used
92101
///
93102
pub async fn create_account(
94103
&self,
95104
seed_phrase: &str,
96-
account_index: u32,
105+
account_hd_index: u32,
97106
birthday_height: Option<u32>,
98-
) -> Result<String, Error> {
107+
) -> Result<u32, Error> {
99108
tracing::info!("Create account called");
100109
self.inner
101-
.create_account(seed_phrase, account_index, birthday_height)
110+
.create_account(seed_phrase, account_hd_index, birthday_height)
102111
.await
112+
.map(|id| *id)
103113
}
104114

105-
pub async fn import_ufvk(
106-
&self,
107-
key: &str,
108-
birthday_height: Option<u32>,
109-
) -> Result<String, Error> {
115+
pub async fn import_ufvk(&self, key: &str, birthday_height: Option<u32>) -> Result<u32, Error> {
110116
let ufvk = UnifiedFullViewingKey::decode(&self.inner.network, key)
111117
.map_err(Error::KeyParseError)?;
112118

113-
self.inner.import_ufvk(&ufvk, birthday_height).await
119+
self.inner
120+
.import_ufvk(&ufvk, birthday_height)
121+
.await
122+
.map(|id| *id)
114123
}
115124

116125
pub async fn suggest_scan_ranges(&self) -> Result<Vec<BlockRange>, Error> {
117126
self.inner.suggest_scan_ranges().await
118127
}
119128

120-
/// Synchronize the wallet with the blockchain up to the tip
121-
/// The passed callback will be called for every batch of blocks processed with the current progress
122-
pub async fn sync(&self, callback: &js_sys::Function) -> Result<(), Error> {
123-
let callback = move |scanned_to: BlockHeight, tip: BlockHeight| {
124-
let this = JsValue::null();
125-
let _ = callback.call2(
126-
&this,
127-
&JsValue::from(Into::<u32>::into(scanned_to)),
128-
&JsValue::from(Into::<u32>::into(tip)),
129-
);
130-
};
131-
132-
self.inner.sync(callback).await?;
133-
134-
Ok(())
135-
}
136-
137129
/// Synchronize the wallet with the blockchain up to the tip using zcash_client_backend's algo
138-
pub async fn sync2(&self) -> Result<(), Error> {
130+
pub async fn sync(&self) -> Result<(), Error> {
139131
assert!(!thread::is_web_worker_thread());
140132

141133
let db = self.inner.clone();
142134

143135
let sync_handler = thread::Builder::new()
144-
.name("sync2".to_string())
136+
.name("sync".to_string())
145137
.spawn_async(|| async {
146138
assert!(thread::is_web_worker_thread());
147139
tracing::debug!(
@@ -150,7 +142,7 @@ impl WebWallet {
150142
);
151143

152144
let db = db;
153-
db.sync2().await.unwrap_throw();
145+
db.sync().await.unwrap_throw();
154146
})
155147
.unwrap_throw()
156148
.join_async();
@@ -187,26 +179,51 @@ impl WebWallet {
187179
}
188180

189181
///
190-
/// Create a transaction proposal to send funds from the wallet to a given address and if approved will sign it and send the proposed transaction(s) to the network
191-
///
192-
/// First a proposal is created by selecting inputs and outputs to cover the requested amount. This proposal is then sent to the approval callback.
193-
/// This allows wallet developers to display a confirmation dialog to the user before continuing.
182+
/// Create a transaction proposal to send funds from the wallet to a given address.
194183
///
195-
/// # Arguments
196-
///
197-
pub async fn transfer(
184+
pub async fn propose_transfer(
198185
&self,
199-
seed_phrase: &str,
200-
from_account_index: usize,
186+
account_id: u32,
201187
to_address: String,
202188
value: u64,
203-
) -> Result<(), Error> {
189+
) -> Result<Proposal, Error> {
204190
let to_address = ZcashAddress::try_from_encoded(&to_address)?;
205-
self.inner
206-
.transfer(seed_phrase, from_account_index, to_address, value)
207-
.await
191+
let proposal = self
192+
.inner
193+
.propose_transfer(AccountId::from(account_id), to_address, value)
194+
.await?;
195+
Ok(proposal.into())
208196
}
209197

198+
///
199+
/// Perform the proving and signing required to create one or more transaction from the proposal.
200+
/// Created transactions are stored in the wallet database and a list of the IDs is returned
201+
///
202+
pub async fn create_proposed_transactions(
203+
&self,
204+
proposal: Proposal,
205+
seed_phrase: &str,
206+
) -> Result<JsValue, Error> {
207+
let usk = usk_from_seed_str(seed_phrase, 0, &self.inner.network)?;
208+
let txids = self
209+
.inner
210+
.create_proposed_transactions(proposal.into(), &usk)
211+
.await?;
212+
Ok(serde_wasm_bindgen::to_value(&txids).unwrap())
213+
}
214+
215+
///
216+
/// Send a list of transactions to the network via the lightwalletd instance this wallet is connected to
217+
///
218+
pub async fn send_authorized_transactions(&self, txids: JsValue) -> Result<(), Error> {
219+
let txids: NonEmpty<TxId> = serde_wasm_bindgen::from_value(txids).unwrap();
220+
self.inner.send_authorized_transactions(&txids).await
221+
}
222+
223+
///////////////////////////////////////////////////////////////////////////////////////
224+
// lightwalletd gRPC methods
225+
///////////////////////////////////////////////////////////////////////////////////////
226+
210227
/// Forwards a call to lightwalletd to retrieve the height of the latest block in the chain
211228
pub async fn get_latest_block(&self) -> Result<u64, Error> {
212229
self.client()

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ pub enum Error {
5959
SqliteError(#[from] zcash_client_sqlite::error::SqliteClientError),
6060
#[error("Invalid seed phrase")]
6161
InvalidSeedPhrase,
62+
#[error("Failed when creating transaction")]
63+
FailedToCreateTransaction,
6264
}
6365

6466
impl From<Error> for JsValue {

src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ pub mod wallet;
1515
pub use wallet::Wallet;
1616

1717
use wasm_bindgen::prelude::*;
18-
use zcash_client_memory::MemoryWalletDb;
19-
use zcash_primitives::consensus;
2018

2119
/// The maximum number of checkpoints to store in each shard-tree
2220
pub const PRUNING_DEPTH: usize = 100;
2321

22+
23+
use zcash_client_memory::MemoryWalletDb;
24+
use zcash_primitives::consensus;
25+
2426
#[cfg(feature = "wasm-parallel")]
2527
pub use wasm_bindgen_rayon::init_thread_pool;
28+
2629
// dummy NO-OP init_thread pool to maintain the same API between features
2730
#[cfg(not(feature = "wasm-parallel"))]
2831
#[wasm_bindgen(js_name = initThreadPool)]
@@ -31,6 +34,5 @@ pub fn init_thread_pool(_threads: usize) {}
3134
#[wasm_bindgen]
3235
pub struct BlockRange(pub u32, pub u32);
3336

34-
pub type MemoryWallet<T> = Wallet<MemoryWalletDb<consensus::Network>, T>;
3537

3638
pub mod sync3;

0 commit comments

Comments
 (0)