Skip to content

Commit

Permalink
Merge pull request #33 from ChainSafe/willem/improved-key-handling
Browse files Browse the repository at this point in the history
Updated transaction proposal and approval flow
  • Loading branch information
ec2 authored Oct 1, 2024
2 parents 62dc517 + 38aa204 commit ce39263
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 79 deletions.
36 changes: 24 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ tokio_with_wasm = { version = "0.7.1", features = ["rt", "rt-multi-thread", "syn

## Zcash dependencies

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

## gRPC Web dependencies
prost = { version = "0.12", default-features = false }
Expand All @@ -77,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 = "d1fa3e846c5e61de3f1df23dd9f4d5416915631a", 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: 8 additions & 2 deletions packages/demo-wallet/src/App/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export async function triggerTransfer(
}

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

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);
}
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()
}
}
64 changes: 44 additions & 20 deletions src/bindgen/wallet.rs
Original file line number Diff line number Diff line change
@@ -1,25 +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, 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::WalletRead;
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::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 @@ -168,31 +172,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
/// Create a transaction proposal to send funds from the wallet to a given address.
///
/// 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.
///
/// # Arguments
///
pub async fn transfer(
pub async fn propose_transfer(
&self,
seed_phrase: &str,
from_account_id: u32,
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,
AccountId::from(from_account_id),
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
Loading

0 comments on commit ce39263

Please sign in to comment.