Skip to content

Commit

Permalink
Merge branch 'main' into ec2/sync3
Browse files Browse the repository at this point in the history
  • Loading branch information
ec2 committed Oct 1, 2024
2 parents 46c4ed2 + f8d6049 commit 3dde460
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 297 deletions.
17 changes: 8 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ native = ["tonic/channel", "tonic/gzip", "tonic/tls-webpki-roots", "tokio/macros
sqlite-db = ["dep:zcash_client_sqlite"]
console_error_panic_hook = ["dep:console_error_panic_hook"]
no-bundler = ["wasm-bindgen-rayon?/no-bundler", "wasm_thread/no-bundler"]
sync2 = []
sync3 = []

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

## Zcash dependencies

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


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

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

getrandom = { version = "0.2", features = ["js"] }
thiserror = "1.0.63"
Expand Down
10 changes: 5 additions & 5 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ default:
just --list

build *features:
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"
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"

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

# sync message board in the web: addigional args: sync2
# sync message board in the web: addigional args:
test-message-board-web *features:
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

# simple example in the web: additional args: sync2
# simple example in the web: additional args:
test-simple-web *features:
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


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

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

Expand Down
17 changes: 12 additions & 5 deletions packages/demo-wallet/src/App/Actions.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import initWasm, { initThreadPool, WebWallet } from "@webzjs/webz-core";

import { State, Action } from "./App";
import { MAINNET_LIGHTWALLETD_PROXY } from "./constants";
import { MAINNET_LIGHTWALLETD_PROXY } from "./Constants";

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

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

Expand Down Expand Up @@ -60,6 +60,13 @@ export async function triggerTransfer(
throw new Error("No active account");
}

await state.webWallet?.transfer(state.accountSeeds[state.activeAccount], state.activeAccount, toAddress, amount);
await syncStateWithWallet(state, dispatch);
let activeAccountSeedPhrase = state.accountSeeds.get(state.activeAccount) || "";

let proposal = await state.webWallet?.propose_transfer(state.activeAccount, toAddress, amount);
console.log(JSON.stringify(proposal.describe(), null, 2));

let txids = await state.webWallet.create_proposed_transactions(proposal, activeAccountSeedPhrase);
console.log(JSON.stringify(txids, null, 2));

await state.webWallet.send_authorized_transactions(txids);
}
10 changes: 5 additions & 5 deletions packages/demo-wallet/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ export type State = {
activeAccount?: number;
summary?: WalletSummary;
chainHeight?: bigint;
accountSeeds: string[];
accountSeeds: Map<number, string>;
};

const initialState: State = {
activeAccount: undefined,
summary: undefined,
chainHeight: undefined,
accountSeeds: [],
accountSeeds: new Map<number, string>(),
};

export type Action =
| { type: "set-active-account"; payload: number }
| { type: "append-account-seed"; payload: string }
| { type: "add-account-seed"; payload: [number, string] }
| { type: "set-web-wallet"; payload: WebWallet }
| { type: "set-summary"; payload: WalletSummary }
| { type: "set-chain-height"; payload: bigint };
Expand All @@ -45,8 +45,8 @@ const reducer = (state: State, action: Action): State => {
case "set-active-account": {
return { ...state, activeAccount: action.payload };
}
case "append-account-seed": {
return { ...state, accountSeeds: [...state.accountSeeds, action.payload] };
case "add-account-seed": {
return { ...state, accountSeeds: state.accountSeeds.set(action.payload[0], action.payload[1]) };
}
case "set-web-wallet": {
return { ...state, webWallet: action.payload };
Expand Down
1 change: 1 addition & 0 deletions packages/demo-wallet/src/App/components/ImportAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function ImportAccount() {
await addNewAccount(state, dispatch, seedPhrase, birthdayHeight);
toast.success("Account imported successfully", {
position: "top-center",
autoClose: 2000,
});
setBirthdayHeight(0);
setSeedPhrase("");
Expand Down
1 change: 1 addition & 0 deletions src/bindgen/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod proposal;
pub mod wallet;
32 changes: 32 additions & 0 deletions src/bindgen/proposal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use wasm_bindgen::prelude::*;

use super::wallet::NoteRef;
use zcash_primitives::transaction::fees::zip317::FeeRule;

/// A handler to an immutable proposal. This can be passed to `create_proposed_transactions` to prove/authorize the transactions
/// before they are sent to the network.
///
/// The proposal can be reviewed by calling `describe` which will return a JSON object with the details of the proposal.
#[wasm_bindgen]
pub struct Proposal {
inner: zcash_client_backend::proposal::Proposal<FeeRule, NoteRef>,
}

impl From<zcash_client_backend::proposal::Proposal<FeeRule, NoteRef>> for Proposal {
fn from(inner: zcash_client_backend::proposal::Proposal<FeeRule, NoteRef>) -> Self {
Self { inner }
}
}

impl From<Proposal> for zcash_client_backend::proposal::Proposal<FeeRule, NoteRef> {
fn from(proposal: Proposal) -> Self {
proposal.inner
}
}

#[wasm_bindgen]
impl Proposal {
pub fn describe(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.inner).unwrap()
}
}
107 changes: 62 additions & 45 deletions src/bindgen/wallet.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
use std::num::NonZeroU32;

use nonempty::NonEmpty;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

use tonic_web_wasm_client::Client;

use crate::error::Error;
use crate::{BlockRange, MemoryWallet, Wallet, PRUNING_DEPTH};
use crate::wallet::usk_from_seed_str;
use crate::{bindgen::proposal::Proposal, BlockRange, Wallet, PRUNING_DEPTH};
use wasm_thread as thread;
use zcash_address::ZcashAddress;
use zcash_client_backend::data_api::{InputSource, WalletRead};
use zcash_client_backend::proto::service::{
compact_tx_streamer_client::CompactTxStreamerClient, ChainSpec,
};
use zcash_client_memory::MemoryWalletDb;
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_primitives::consensus::{self, BlockHeight};
use zcash_primitives::consensus;
use zcash_primitives::transaction::TxId;

pub type MemoryWallet<T> = Wallet<MemoryWalletDb<consensus::Network>, T>;
pub type AccountId =
<MemoryWalletDb<zcash_primitives::consensus::Network> as WalletRead>::AccountId;
pub type NoteRef = <MemoryWalletDb<zcash_primitives::consensus::Network> as InputSource>::NoteRef;

/// # A Zcash wallet
///
Expand Down Expand Up @@ -87,61 +96,44 @@ impl WebWallet {
///
/// # Arguments
/// seed_phrase - mnemonic phrase to initialise the wallet
/// account_index - The HD derivation index to use. Can be any integer
/// account_hd_index - The HD derivation index to use. Can be any integer
/// birthday_height - The block height at which the account was created, optionally None and the current height is used
///
pub async fn create_account(
&self,
seed_phrase: &str,
account_index: u32,
account_hd_index: u32,
birthday_height: Option<u32>,
) -> Result<String, Error> {
) -> Result<u32, Error> {
tracing::info!("Create account called");
self.inner
.create_account(seed_phrase, account_index, birthday_height)
.create_account(seed_phrase, account_hd_index, birthday_height)
.await
.map(|id| *id)
}

pub async fn import_ufvk(
&self,
key: &str,
birthday_height: Option<u32>,
) -> Result<String, Error> {
pub async fn import_ufvk(&self, key: &str, birthday_height: Option<u32>) -> Result<u32, Error> {
let ufvk = UnifiedFullViewingKey::decode(&self.inner.network, key)
.map_err(Error::KeyParseError)?;

self.inner.import_ufvk(&ufvk, birthday_height).await
self.inner
.import_ufvk(&ufvk, birthday_height)
.await
.map(|id| *id)
}

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

/// Synchronize the wallet with the blockchain up to the tip
/// The passed callback will be called for every batch of blocks processed with the current progress
pub async fn sync(&self, callback: &js_sys::Function) -> Result<(), Error> {
let callback = move |scanned_to: BlockHeight, tip: BlockHeight| {
let this = JsValue::null();
let _ = callback.call2(
&this,
&JsValue::from(Into::<u32>::into(scanned_to)),
&JsValue::from(Into::<u32>::into(tip)),
);
};

self.inner.sync(callback).await?;

Ok(())
}

/// Synchronize the wallet with the blockchain up to the tip using zcash_client_backend's algo
pub async fn sync2(&self) -> Result<(), Error> {
pub async fn sync(&self) -> Result<(), Error> {
assert!(!thread::is_web_worker_thread());

let db = self.inner.clone();

let sync_handler = thread::Builder::new()
.name("sync2".to_string())
.name("sync".to_string())
.spawn_async(|| async {
assert!(thread::is_web_worker_thread());
tracing::debug!(
Expand All @@ -150,7 +142,7 @@ impl WebWallet {
);

let db = db;
db.sync2().await.unwrap_throw();
db.sync().await.unwrap_throw();
})
.unwrap_throw()
.join_async();
Expand Down Expand Up @@ -187,26 +179,51 @@ impl WebWallet {
}

///
/// 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
///
/// First a proposal is created by selecting inputs and outputs to cover the requested amount. This proposal is then sent to the approval callback.
/// This allows wallet developers to display a confirmation dialog to the user before continuing.
/// Create a transaction proposal to send funds from the wallet to a given address.
///
/// # Arguments
///
pub async fn transfer(
pub async fn propose_transfer(
&self,
seed_phrase: &str,
from_account_index: usize,
account_id: u32,
to_address: String,
value: u64,
) -> Result<(), Error> {
) -> Result<Proposal, Error> {
let to_address = ZcashAddress::try_from_encoded(&to_address)?;
self.inner
.transfer(seed_phrase, from_account_index, to_address, value)
.await
let proposal = self
.inner
.propose_transfer(AccountId::from(account_id), to_address, value)
.await?;
Ok(proposal.into())
}

///
/// Perform the proving and signing required to create one or more transaction from the proposal.
/// Created transactions are stored in the wallet database and a list of the IDs is returned
///
pub async fn create_proposed_transactions(
&self,
proposal: Proposal,
seed_phrase: &str,
) -> Result<JsValue, Error> {
let usk = usk_from_seed_str(seed_phrase, 0, &self.inner.network)?;
let txids = self
.inner
.create_proposed_transactions(proposal.into(), &usk)
.await?;
Ok(serde_wasm_bindgen::to_value(&txids).unwrap())
}

///
/// Send a list of transactions to the network via the lightwalletd instance this wallet is connected to
///
pub async fn send_authorized_transactions(&self, txids: JsValue) -> Result<(), Error> {
let txids: NonEmpty<TxId> = serde_wasm_bindgen::from_value(txids).unwrap();
self.inner.send_authorized_transactions(&txids).await
}

///////////////////////////////////////////////////////////////////////////////////////
// lightwalletd gRPC methods
///////////////////////////////////////////////////////////////////////////////////////

/// Forwards a call to lightwalletd to retrieve the height of the latest block in the chain
pub async fn get_latest_block(&self) -> Result<u64, Error> {
self.client()
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub enum Error {
SqliteError(#[from] zcash_client_sqlite::error::SqliteClientError),
#[error("Invalid seed phrase")]
InvalidSeedPhrase,
#[error("Failed when creating transaction")]
FailedToCreateTransaction,
}

impl From<Error> for JsValue {
Expand Down
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ pub mod wallet;
pub use wallet::Wallet;

use wasm_bindgen::prelude::*;
use zcash_client_memory::MemoryWalletDb;
use zcash_primitives::consensus;

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


use zcash_client_memory::MemoryWalletDb;
use zcash_primitives::consensus;

#[cfg(feature = "wasm-parallel")]
pub use wasm_bindgen_rayon::init_thread_pool;

// dummy NO-OP init_thread pool to maintain the same API between features
#[cfg(not(feature = "wasm-parallel"))]
#[wasm_bindgen(js_name = initThreadPool)]
Expand All @@ -31,6 +34,5 @@ pub fn init_thread_pool(_threads: usize) {}
#[wasm_bindgen]
pub struct BlockRange(pub u32, pub u32);

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

pub mod sync3;
Loading

0 comments on commit 3dde460

Please sign in to comment.