Simple demo displaying address of a fixed spending key

This commit is contained in:
Jack Grigg 2019-06-11 00:39:28 +01:00
parent a56c4c2630
commit 4caf14baa2
No known key found for this signature in database
GPG Key ID: 9E8255172BBF9898
7 changed files with 214 additions and 10 deletions

View File

@ -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.

9
build.sh Executable file
View File

@ -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

View File

@ -2,9 +2,20 @@
<html>
<head>
<meta charset="utf-8">
<title>Hello wasm-pack!</title>
<title>Zcash Demo Wallet</title>
</head>
<body>
<h1>Welcome to your demo Zcash wallet!</h1>
<p>You can interact with it using a <code>zcashd</code> testnet node.</p>
<div id="zcash-client-loading">
<h2>Loading...</h2>
</div>
<div id="zcash-client-content" style="display: none">
<h2 id="zcash-client-address"></h2>
<p>That's your Zcash address!</p>
<h2 id="zcash-client-balance"></h2>
<p id="zcash-client-no-balance">You have no TAZ. Go <a href="https://faucet.testnet.z.cash/" target="blank">here</a> to get some!</p>
</div>
<script src="./bootstrap.js"></script>
</body>
</html>

View File

@ -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 = ''
})

View File

@ -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"

View File

@ -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<Bls12>,
witnesses: Vec<IncrementalWitness<Node>>,
nullifier: [u8; 32],
spent: Option<TxId>,
}
impl SaplingNoteData {
fn new(
extfvk: &ExtendedFullViewingKey,
output: zcash_client_backend::wallet::WalletShieldedOutput,
witness: IncrementalWitness<Node>,
) -> 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<SaplingNoteData>,
}
#[wasm_bindgen]
pub struct Client {
extsks: [ExtendedSpendingKey; 1],
extfvks: [ExtendedFullViewingKey; 1],
address: PaymentAddress<Bls12>,
txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
}
/// 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::<u64>()
})
.sum::<u64>() as u32
}
}

View File

@ -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()
}
}