Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

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

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ incrementalmerkletree = "0.8.1"
zcash_spec = "0.2.1"
zip32 = { version = "0.2.0", default-features = false }
visibility = "0.1.1"
wasm-bindgen = { version = "0.2", optional = true }

# Circuit
halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "zsa1", optional = true }
Expand Down Expand Up @@ -79,17 +80,22 @@ inferno = { version = "0.11", default-features = false, features = ["multithread
#clap = "=4.2.0" #Pinned: Used by inferno. Later version requires Rust 1.70
pprof = { version = "0.11", features = ["criterion", "flamegraph"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", default-features = false, features = ["js"] }

[lib]
crate-type = ["cdylib", "rlib"]
bench = false

[features]
default = ["circuit", "multicore", "std"]
default = ["circuit", "std"] # multicore is opt-in; wasm builds disable it to avoid atomics issues
std = ["core2/std", "group/wnaf-memuse", "reddsa/std"]
circuit = ["dep:halo2_gadgets", "dep:halo2_proofs", "std"]
unstable-frost = []
multicore = ["halo2_proofs?/multicore"]
dev-graph = ["halo2_proofs?/dev-graph", "image", "plotters"]
test-dependencies = ["proptest", "rand/std"]
wasm = ["wasm-bindgen"]

[[bench]]
name = "note_decryption"
Expand Down
38 changes: 38 additions & 0 deletions pkg/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "orchard",
"type": "module",
"collaborators": [
"Sean Bowe <sean@electriccoin.co>",
"Jack Grigg <jack@electriccoin.co>",
"Daira-Emma Hopwood <daira@jacaranda.org>",
"Ying Tong Lai",
"Kris Nuttycombe <kris@electriccoin.co>"
],
"description": "The Orchard shielded transaction protocol",
"version": "0.11.0",
"license": "MIT OR Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/zcash/orchard"
},
"files": [
"orchard_bg.wasm",
"orchard.js",
"orchard_bg.js",
"orchard.d.ts",
"LICENSE-APACHE",
"LICENSE-MIT"
],
"main": "orchard.js",
"types": "orchard.d.ts",
"sideEffects": [
"./orchard.js",
"./snippets/*"
],
"keywords": [
"zcash"
],
"dependencies": {
"bip39": "^3.0.4"
}
}
65 changes: 65 additions & 0 deletions pkg/test-derive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node
/*
* Minimal Node.js test for the full Orchard key/address derivation pipeline using a BIP39 mnemonic.
*
* Steps:
* 1) derive_spending_key(seed: Uint8Array, coinType: number, account: number) -> spendingKey
* 2) derive_full_viewing_key(spendingKey: Uint8Array) -> fullViewingKey
* 3) derive_address(fullViewingKey: Uint8Array, diversifierIndex: number) -> rawAddress
*
* Prerequisites:
* npm install bip39
* node --experimental-wasm-modules test-derive.js
*/
import { readFile } from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import * as orchard from './orchard_bg.js';
import bip39 from 'bip39';

// In ES modules, compute __dirname relative to this file
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

async function main() {
// Load and instantiate the WebAssembly module manually
// Resolve the .wasm path relative to this script's directory
const wasmPath = path.resolve(__dirname, 'orchard_bg.wasm');
const wasmBytes = await readFile(wasmPath);
// Provide the two JS functions the .wasm expects as imports
const importObject = {
'./orchard_bg.js': {
__wbindgen_string_new: orchard.__wbindgen_string_new,
__wbindgen_object_drop_ref: orchard.__wbindgen_object_drop_ref,
},
};
const { instance } = await WebAssembly.instantiate(wasmBytes, importObject);
orchard.__wbg_set_wasm(instance.exports);

// Example BIP39 mnemonic (12 words)
const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
// Derive a 64-byte seed from the mnemonic
const seedBuffer = await bip39.mnemonicToSeed(mnemonic);
const seed = new Uint8Array(seedBuffer);

const coinType = 1;
const account = 0;

// Derive the Orchard spending key
const spendingKey = orchard.derive_spending_key(seed, coinType, account);
console.log('Spending key (hex):', Buffer.from(spendingKey).toString('hex'));

// Derive the full viewing key from the spending key
const fullViewingKey = orchard.derive_full_viewing_key(spendingKey);
console.log('Full viewing key (hex):', Buffer.from(fullViewingKey).toString('hex'));

// Derive a raw Orchard payment address (using diversifier index = 0)
const diversifierIndex = 0;
const rawAddress = orchard.derive_address(fullViewingKey, diversifierIndex);
console.log('Raw address (hex):', Buffer.from(rawAddress).toString('hex'));
}

main().catch(err => {
console.error(err);
process.exit(1);
});
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,11 @@ impl Proof {
Proof(bytes)
}
}
#[cfg(test)]
mod key_test;

// WASM-bindgen: derive Orchard address from a spending key
#[cfg(feature = "wasm")]
mod wasm_bindings;
#[cfg(feature = "wasm")]
pub use wasm_bindings::derive_orchard_address;
Loading