Skip to content
Open
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
1,848 changes: 1,059 additions & 789 deletions Cargo.lock

Large diffs are not rendered by default.

45 changes: 15 additions & 30 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "zcash-devtool"
version = "0.1.0"
edition = "2021"
rust-version = "1.81"
rust-version = "1.85.1"
license = "MIT OR Apache-2.0"
publish = false

Expand Down Expand Up @@ -34,20 +34,20 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
uuid = "1"

# Zcash
orchard = { version = "0.12", default-features = false }
pczt = "0.5"
sapling = { package = "sapling-crypto", version = "0.6" }
transparent = { package = "zcash_transparent", version = "0.6", features = ["test-dependencies"] }
zcash_address = "0.10"
zcash_client_backend = { version = "0.21", features = ["lightwalletd-tonic-tls-webpki-roots", "orchard", "pczt", "tor"] }
zcash_client_sqlite = { version = "0.19", features = ["unstable", "orchard", "serde"] }
zcash_keys = { version = "0.12", features = ["unstable", "orchard"] }
zcash_primitives = "0.26"
zcash_proofs = { version = "0.26", features = ["bundled-prover"] }
zcash_protocol = { version = "0.7", features = ["local-consensus"] }
orchard = { version = "0.14", default-features = false }
pczt = "0.7"
sapling = { package = "sapling-crypto", version = "0.7" }
transparent = { package = "zcash_transparent", version = "0.8", features = ["test-dependencies"] }
zcash_address = "0.12"
zcash_client_backend = { version = "0.23", features = ["lightwalletd-tonic-tls-webpki-roots", "orchard", "pczt", "tor"] }
zcash_client_sqlite = { version = "0.21", features = ["unstable", "orchard", "serde"] }
zcash_keys = { version = "0.14", features = ["unstable", "orchard"] }
zcash_primitives = "0.28"
zcash_proofs = { version = "0.28", features = ["bundled-prover"] }
zcash_protocol = { version = "0.9", features = ["local-consensus"] }
zcash_script = "0.4"
zip32 = "0.2"
zip321 = "0.6"
zip321 = "0.8"

# CLI
clap = { version = "4.5", features = ["derive", "string", "unstable-styles"] }
Expand All @@ -70,13 +70,13 @@ bellman = "0.14"
blake2b_simd = "1"
bs58 = "0.5"
ed25519-zebra = "4"
equihash = "0.2"
equihash = "0.3"
group = "0.13"
lazy_static = "1"
serde_json = "1"
sha2 = "0.10"
uint = "0.9"
zcash_encoding = "0.3"
zcash_encoding = "0.4"
zcash_note_encryption = "0.4.1"

# PCZT QR codes
Expand Down Expand Up @@ -112,18 +112,3 @@ tui = [
"dep:tokio-util",
"dep:tui-logger",
]

[patch.crates-io]
equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
f4jumble = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
pczt = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "77c422f1bd56400c9647f089c87f3776e16fd212" }
3 changes: 1 addition & 2 deletions src/commands/create_multisig_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use sha2::{Digest, Sha256};

use transparent::address::TransparentAddress;
use zcash_keys::encoding::AddressCodec;
use zcash_protocol::consensus::Network;
use zcash_script::{
pattern::check_multisig,
script::{self, Evaluable},
Expand Down Expand Up @@ -54,7 +53,7 @@ impl Command {
} = self;

let (multisig_script, addr) = multisig_script(threshold, pub_keys)?;
let addr = addr.encode(&Network::from(network));
let addr = addr.encode(&network);
println!("Created multisig address: {addr}");
println!("Redeem script: {}", hex::encode(multisig_script.to_bytes()));

Expand Down
2 changes: 2 additions & 0 deletions src/commands/inspect/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ pub(crate) fn inspect_sapling_extsk(data: Vec<u8>, network: NetworkType) {
nu5: None,
nu6: None,
nu6_1: None,
nu6_2: None,
}),
};
eprintln!("- UFVK: {encoded_ufvk}");
Expand All @@ -215,6 +216,7 @@ pub(crate) fn inspect_sapling_extsk(data: Vec<u8>, network: NetworkType) {
nu5: None,
nu6: None,
nu6_1: None,
nu6_2: None,
}),
};
eprintln!(" - Default address: {encoded_ua}");
Expand Down
2 changes: 2 additions & 0 deletions src/commands/inspect/keys/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub(crate) fn inspect_sapling_extfvk(data: Vec<u8>, network: NetworkType) {
nu5: None,
nu6: None,
nu6_1: None,
nu6_2: None,
}),
};
eprintln!("- Equivalent UFVK: {encoded_ufvk}");
Expand All @@ -139,6 +140,7 @@ pub(crate) fn inspect_sapling_extfvk(data: Vec<u8>, network: NetworkType) {
nu5: None,
nu6: None,
nu6_1: None,
nu6_2: None,
}),
};
eprintln!(" - Default address: {encoded_ua}");
Expand Down
4 changes: 3 additions & 1 deletion src/commands/inspect/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,15 @@ pub(crate) fn inspect(
tx,
&SignableInput::Transparent(
::transparent::sighash::SignableInput::from_parts(
bundle,
hash_type,
i,
// For P2PKH these are the same.
coin.script_pubkey(),
coin.script_pubkey(),
coin.value(),
),
)
.expect("input index is within range"),
),
txid_parts,
);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/pczt/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl Command {
None,
vec![],
)
.ok_or_else(|| error::Error::TransparentMemo(0))?])
.map_err(|_| error::Error::TransparentMemo(0))?])
.map_err(error::Error::from)?;

let proposal = propose_transfer(
Expand All @@ -103,6 +103,7 @@ impl Command {
&change_strategy,
request,
ConfirmationsPolicy::default(),
None,
)
.map_err(error::Error::from)?;

Expand Down
2 changes: 1 addition & 1 deletion src/commands/pczt/create_manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub(crate) struct Command {
impl Command {
pub(crate) async fn run(self, wallet_dir: Option<String>) -> anyhow::Result<()> {
let params = if let Some(network) = self.network {
consensus::Network::from(network)
network
} else {
let config = WalletConfig::read(wallet_dir.as_ref())?;
config.network()
Expand Down
10 changes: 7 additions & 3 deletions src/commands/pczt/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,8 @@ impl Command {
}

match pczt.into_effects() {
None => println!("Not enough information to build the transaction's effects"),
Some(tx_data) => {
Err(e) => println!("Could not build the transaction's effects: {e:?}"),
Ok(tx_data) => {
println!();

let txid_parts = tx_data.digest(TxIdDigester);
Expand Down Expand Up @@ -396,12 +396,16 @@ impl Command {
&tx_data,
&SignableInput::Transparent(
transparent::sighash::SignableInput::from_parts(
tx_data
.transparent_bundle()
.expect("transparent bundle present"),
hash_type,
index,
&redeem_script.as_ref().unwrap_or(&script_pubkey).into(), // for p2pkh, always the same as script_pubkey
&script_pubkey.into(),
value,
),
)
.expect("input index is within range"),
),
&txid_parts,
);
Expand Down
2 changes: 1 addition & 1 deletion src/commands/pczt/pay_manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub(crate) struct Command {
impl Command {
pub(crate) async fn run(self, wallet_dir: Option<String>) -> anyhow::Result<()> {
let params = if let Some(network) = self.network {
consensus::Network::from(network)
network
} else {
let config = WalletConfig::read(wallet_dir.as_ref())?;
config.network()
Expand Down
3 changes: 2 additions & 1 deletion src/commands/pczt/shield.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use zcash_client_backend::{
create_pczt_from_proposal, input_selection::GreedyInputSelector, propose_shielding,
ConfirmationsPolicy,
},
Account as _, WalletRead,
Account as _, TransparentOutputFilter, WalletRead,
},
fees::{standard::MultiOutputChangeStrategy, DustOutputPolicy, SplitPolicy, StandardFeeRule},
wallet::OvkPolicy,
Expand Down Expand Up @@ -97,6 +97,7 @@ impl Command {
&from_addrs,
account.id(),
confirmations_policy,
TransparentOutputFilter::All,
)
.map_err(error::Error::Shield)?;

Expand Down
4 changes: 4 additions & 0 deletions src/commands/wallet.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::Subcommand;

pub(crate) mod balance;
pub(crate) mod derive_address;
pub(crate) mod derive_path;
pub(crate) mod display_mnemonic;
pub(crate) mod enhance;
Expand Down Expand Up @@ -66,6 +67,9 @@ pub(crate) enum Command {
/// Derive key material at a particular path below the wallet seed
DerivePath(derive_path::Command),

/// Derive an account's default addresses from a mnemonic, offline (no chain or wallet needed)
DeriveAddress(derive_address::Command),

/// List the transactions in the wallet
ListTx(list_tx::Command),

Expand Down
58 changes: 58 additions & 0 deletions src/commands/wallet/derive_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Offline derivation of a wallet's default addresses from a mnemonic.
//!
//! Unlike the other wallet commands this needs no chain and no on-disk wallet — it derives the
//! account's default unified address and its transparent receiver straight from the mnemonic. It
//! exists so automated regtest funding can learn the funding wallet's transparent address *before*
//! a chain exists (zebra needs it as its coinbase `miner_address` at launch), keeping the funder on
//! a single chain so its wallet birthday anchor stays valid.

use anyhow::anyhow;
use bip0039::{English, Mnemonic};
use clap::Args;
use secrecy::Zeroize;
use zcash_keys::{
encoding::AddressCodec,
keys::{UnifiedAddressRequest, UnifiedSpendingKey},
};

use crate::data::Network;

// Options accepted for the `derive-address` command
#[derive(Debug, Args)]
pub(crate) struct Command {
/// The mnemonic phrase to derive from.
#[arg(long)]
mnemonic: String,

/// The network: "main", "test" or "regtest".
#[arg(short, long)]
#[arg(value_parser = Network::parse)]
network: Network,
}

impl Command {
pub(crate) fn run(self) -> anyhow::Result<()> {
let params = self.network;

let mnemonic = <Mnemonic<English>>::from_phrase(self.mnemonic.trim())
.map_err(|e| anyhow!("invalid mnemonic: {e}"))?;
let mut seed = mnemonic.to_seed("");
let usk = UnifiedSpendingKey::from_seed(&params, &seed, zip32::AccountId::ZERO)
.map_err(|e| anyhow!("failed to derive spending key: {e}"))?;
seed.zeroize();

// The default address of the first account — exactly what `create_account` produces and
// what the wallet scans, so a coinbase mined to this transparent receiver is detected.
let (ua, _) = usk
.to_unified_full_viewing_key()
.default_address(UnifiedAddressRequest::AllAvailableKeys)
.map_err(|e| anyhow!("failed to derive default address: {e}"))?;

println!("Unified Address: {}", ua.encode(&params));
if let Some(taddr) = ua.transparent() {
println!("Transparent Address: {}", taddr.encode(&params));
}

Ok(())
}
}
8 changes: 6 additions & 2 deletions src/commands/wallet/enhance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ use zcash_client_backend::{
use zcash_client_sqlite::{util::SystemClock, WalletDb};
use zcash_keys::encoding::AddressCodec;
use zcash_primitives::transaction::{Transaction, TxId};
use zcash_protocol::consensus::{BlockHeight, BranchId, Network};
use zcash_protocol::consensus::{BlockHeight, BranchId};

use crate::{config::get_wallet_network, data::get_db_paths, remote::ConnectionArgs};
use crate::{
config::get_wallet_network,
data::{get_db_paths, Network},
remote::ConnectionArgs,
};

// Options accepted for the `enhance` command
#[derive(Debug, Args)]
Expand Down
15 changes: 6 additions & 9 deletions src/commands/wallet/import_ufvk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ use zcash_client_backend::{
};
use zcash_client_sqlite::{util::SystemClock, WalletDb};
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_protocol::consensus;
use zip32::fingerprint::SeedFingerprint;

use crate::{data::get_db_paths, error, parse_hex, remote::ConnectionArgs};
use crate::{
data::{get_db_paths, Network},
error, parse_hex,
remote::ConnectionArgs,
};

// Options accepted for the `import-ufvk` command
#[derive(Debug, Args)]
Expand Down Expand Up @@ -45,13 +48,7 @@ impl Command {
let (network, ufvk) = unified::Ufvk::decode(&self.ufvk)?;
let ufvk = UnifiedFullViewingKey::parse(&ufvk).map_err(|e| anyhow!("{e}"))?;

let params = match network {
consensus::NetworkType::Main => Ok(consensus::Network::MainNetwork),
consensus::NetworkType::Test => Ok(consensus::Network::TestNetwork),
consensus::NetworkType::Regtest => {
Err(anyhow!("UFVK is for regtest, which is unsupported"))
}
}?;
let params = Network::from(network);

let (_, db_data) = get_db_paths(wallet_dir.as_ref());
let mut db_data = WalletDb::for_path(db_data, params, SystemClock, OsRng)?;
Expand Down
Loading
Loading