From 4caf14baa299a3e5493a25787ec395da210481c7 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 11 Jun 2019 00:39:28 +0100 Subject: [PATCH] Simple demo displaying address of a fixed spending key --- README.md | 21 ++++++ build.sh | 9 +++ demo-www/index.html | 13 +++- demo-www/index.js | 26 ++++++- zcash-client-backend-wasm/Cargo.toml | 16 ++++ zcash-client-backend-wasm/src/lib.rs | 106 ++++++++++++++++++++++++++- zcash-client-sdk-js/src/index.js | 33 ++++++++- 7 files changed, 214 insertions(+), 10 deletions(-) create mode 100755 build.sh diff --git a/README.md b/README.md index f250f5b..61702b2 100644 --- a/README.md +++ b/README.md @@ -1 +1,22 @@ # Zcon1 WASM demo + +## Dependencies + +- [Rust](https://www.rust-lang.org/tools/install) +- [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) +- [npm](https://www.npmjs.com/get-npm) + +## Building + +```sh +$ ./build.sh +``` + +## Running + +```sh +$ cd demo-www +$ npm run start +``` + +Then open http://localhost:8080/ in your browser. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b7d721e --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +BASE_DIR=$(pwd) + +cd "$BASE_DIR/zcash-client-backend-wasm" +wasm-pack build +cd "$BASE_DIR/zcash-client-sdk-js" +npm install +cd "$BASE_DIR/demo-www" +npm install diff --git a/demo-www/index.html b/demo-www/index.html index ab7dfaf..d88c5d5 100644 --- a/demo-www/index.html +++ b/demo-www/index.html @@ -2,9 +2,20 @@ - Hello wasm-pack! + Zcash Demo Wallet +

Welcome to your demo Zcash wallet!

+

You can interact with it using a zcashd testnet node.

+
+

Loading...

+
+ diff --git a/demo-www/index.js b/demo-www/index.js index 98ac117..af97a11 100644 --- a/demo-www/index.js +++ b/demo-www/index.js @@ -1,3 +1,25 @@ -import * as wasm from 'zcash-client-sdk' +import { ZcashClient } from 'zcash-client-sdk' -wasm.greet(); +const address = document.getElementById('zcash-client-address') +const balance = document.getElementById('zcash-client-balance') +const noBalance = document.getElementById('zcash-client-no-balance') + +var zcashClient = new ZcashClient({ + setAddress: (newAddress) => { + address.textContent = newAddress + }, + updateBalance: (newBalance) => { + balance.textContent = `Balance: ${newBalance} TAZ` + if (newBalance > 0) { + noBalance.style.display = 'none' + } else { + noBalance.style.display = '' + } + } +}) + +zcashClient.load(() => { + // Loading complete, show the wallet + document.getElementById('zcash-client-loading').remove() + document.getElementById('zcash-client-content').style.display = '' +}) diff --git a/zcash-client-backend-wasm/Cargo.toml b/zcash-client-backend-wasm/Cargo.toml index 33ed376..e6e70f8 100644 --- a/zcash-client-backend-wasm/Cargo.toml +++ b/zcash-client-backend-wasm/Cargo.toml @@ -26,6 +26,22 @@ console_error_panic_hook = { version = "0.1.1", optional = true } # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. wee_alloc = { version = "0.4.2", optional = true } +[dependencies.pairing] +git = "https://github.com/str4d/librustzcash.git" +branch = "demo-wasm" + +[dependencies.sapling-crypto] +git = "https://github.com/str4d/librustzcash.git" +branch = "demo-wasm" + +[dependencies.zcash_client_backend] +git = "https://github.com/str4d/librustzcash.git" +branch = "demo-wasm" + +[dependencies.zcash_primitives] +git = "https://github.com/str4d/librustzcash.git" +branch = "demo-wasm" + [dev-dependencies] wasm-bindgen-test = "0.2" diff --git a/zcash-client-backend-wasm/src/lib.rs b/zcash-client-backend-wasm/src/lib.rs index a48aba5..0b2f979 100644 --- a/zcash-client-backend-wasm/src/lib.rs +++ b/zcash-client-backend-wasm/src/lib.rs @@ -1,5 +1,20 @@ mod utils; +use pairing::bls12_381::Bls12; +use sapling_crypto::primitives::{Note, PaymentAddress}; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use zcash_client_backend::{ + constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::encode_payment_address, +}; +use zcash_primitives::{ + merkle_tree::IncrementalWitness, + sapling::Node, + transaction::TxId, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + JUBJUB, +}; + use wasm_bindgen::prelude::*; // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global @@ -9,11 +24,94 @@ use wasm_bindgen::prelude::*; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[wasm_bindgen] -extern { +extern "C" { fn alert(s: &str); } -#[wasm_bindgen] -pub fn greet() { - alert("Hello, zcash-client-backend-wasm!"); +struct SaplingNoteData { + account: usize, + note: Note, + witnesses: Vec>, + nullifier: [u8; 32], + spent: Option, +} + +impl SaplingNoteData { + fn new( + extfvk: &ExtendedFullViewingKey, + output: zcash_client_backend::wallet::WalletShieldedOutput, + witness: IncrementalWitness, + ) -> Self { + let nf = { + let mut nf = [0; 32]; + nf.copy_from_slice( + &output + .note + .nf(&extfvk.fvk.vk, witness.position() as u64, &JUBJUB), + ); + nf + }; + + SaplingNoteData { + account: output.account, + note: output.note, + witnesses: vec![], + nullifier: nf, + spent: None, + } + } +} + +struct WalletTx { + block: i32, + notes: Vec, +} + +#[wasm_bindgen] +pub struct Client { + extsks: [ExtendedSpendingKey; 1], + extfvks: [ExtendedFullViewingKey; 1], + address: PaymentAddress, + txs: Arc>>, +} + +/// Public methods, exported to JavaScript. +#[wasm_bindgen] +impl Client { + pub fn new() -> Self { + utils::set_panic_hook(); + + let extsk = ExtendedSpendingKey::master(&[0; 32]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let address = extfvk.default_address().unwrap().1; + + Client { + extsks: [extsk], + extfvks: [extfvk], + address, + txs: Arc::new(RwLock::new(HashMap::new())), + } + } + + pub fn address(&self) -> String { + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &self.address) + } + + // TODO: This will be inaccurate if the balance exceeds a u32, but u64 -> JavaScript + // requires BigUint64Array which has limited support across browsers, and is not + // implemented in the LTS version of Node.js. For now, let's assume that no one is + // going to use a web wallet with more than ~21 TAZ. + pub fn balance(&self) -> u32 { + self.txs + .read() + .unwrap() + .values() + .map(|tx| { + tx.notes + .iter() + .map(|nd| if nd.spent.is_none() { nd.note.value } else { 0 }) + .sum::() + }) + .sum::() as u32 + } } diff --git a/zcash-client-sdk-js/src/index.js b/zcash-client-sdk-js/src/index.js index fd27fff..85f3d54 100644 --- a/zcash-client-sdk-js/src/index.js +++ b/zcash-client-sdk-js/src/index.js @@ -1,5 +1,32 @@ -import * as wasm from 'zcash-client-backend-wasm' +import { Client } from 'zcash-client-backend-wasm' -export function greet () { - wasm.greet() +const COIN = 100000000 + +export class ZcashClient { + constructor (uiHandlers) { + this.client = Client.new() + this.uiHandlers = uiHandlers + } + + updateUI () { + this.uiHandlers.updateBalance(this.client.balance() / COIN) + } + + load (onFinished) { + var self = this + + var loader = () => { + // Register event handlers + + // Initial UI updates + self.uiHandlers.setAddress(self.client.address()) + self.updateUI() + + // Finished loading! + onFinished() + } + + // document.addEventListener('DOMContentLoaded', loader, false) + loader() + } }