diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..971a4b5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "rust-analyzer.check.features": null, + "rust-analyzer.cargo.features": "all" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 39d6a02..1a252f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,7 +289,7 @@ dependencies = [ "derive-deftly", "derive_builder_fork_arti", "derive_more", - "educe", + "educe 0.4.23", "fs-mistrust", "futures", "hostname-validator", @@ -1800,12 +1800,24 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ - "enum-ordinalize", + "enum-ordinalize 3.1.15", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize 4.3.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "either" version = "1.13.0" @@ -1856,6 +1868,26 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "equihash" version = "0.2.0" @@ -2520,6 +2552,19 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hidapi" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "pkg-config", + "windows-sys 0.48.0", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -3101,6 +3146,75 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +[[package]] +name = "ledger-apdu" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c21ffd28d97c9252671ab2ebe7078c9fa860ff3c5a125039e174d25ec6872169" +dependencies = [ + "arrayref", + "no-std-compat", + "snafu", +] + +[[package]] +name = "ledger-transport" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f18de77d956a030dbc5869ced47d404bbd641216ef2f9dce7ca90833ca64ff" +dependencies = [ + "async-trait", + "ledger-apdu", +] + +[[package]] +name = "ledger-transport-hid" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e34341e2708fbf805a9ada44ef6182170c6464c4fc068ab801abb7562fd5e8" +dependencies = [ + "byteorder", + "cfg-if 1.0.0", + "hex", + "hidapi", + "ledger-transport", + "libc", + "log", + "thiserror 1.0.69", +] + +[[package]] +name = "ledger-zcash" +version = "2.0.0" +source = "git+https://github.com/zecdev/ledger-zcash-rs.git?rev=d12c85b7b2d12bd860cee55fc79cb46330259c56#d12c85b7b2d12bd860cee55fc79cb46330259c56" +dependencies = [ + "byteorder", + "cfg-if 1.0.0", + "educe 0.5.11", + "hex", + "lazy_static", + "ledger-transport", + "ledger-zondax-generic", + "log", + "serde", + "sha2", + "thiserror 1.0.69", + "tokio", + "zx-bip44", +] + +[[package]] +name = "ledger-zondax-generic" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b7e1b78719fff5e70a67bab7224b1ed917aebb2b38521ffa047a9d93cc0501" +dependencies = [ + "async-trait", + "ledger-transport", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "libc" version = "0.2.169" @@ -3124,7 +3238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3415,6 +3529,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "nokhwa" version = "0.10.7" @@ -3723,6 +3843,7 @@ dependencies = [ "memuse", "nonempty", "pasta_curves", + "proptest", "rand", "reddsa", "serde", @@ -4499,8 +4620,7 @@ dependencies = [ [[package]] name = "redjubjub" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" +source = "git+https://github.com/ZcashFoundation/redjubjub?rev=1cf76ae8c02ec0b2627e20b60df526641d40c942#1cf76ae8c02ec0b2627e20b60df526641d40c942" dependencies = [ "rand_core", "reddsa", @@ -4911,7 +5031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5738eff432cfb1f861b0d7d2cfc7c3fabef53b8585840f16ce2eb980c782133b" dependencies = [ "derive_more", - "educe", + "educe 0.4.23", "either", "fluid-let", "thiserror 2.0.8", @@ -4948,7 +5068,7 @@ dependencies = [ [[package]] name = "sapling-crypto" version = "0.4.0" -source = "git+https://github.com/zcash/sapling-crypto.git?rev=3c2235747553da642fb142d1eeb9b1afa8391987#3c2235747553da642fb142d1eeb9b1afa8391987" +source = "git+https://github.com/pacu/sapling-crypto.git?rev=09320f2fb1d058185d5a739325e4b893b0012955#09320f2fb1d058185d5a739325e4b893b0012955" dependencies = [ "aes", "bellman", @@ -4956,7 +5076,7 @@ dependencies = [ "blake2b_simd", "blake2s_simd", "bls12_381", - "byteorder", + "core2", "document-features", "ff", "fpe", @@ -4967,11 +5087,13 @@ dependencies = [ "jubjub", "lazy_static", "memuse", + "proptest", "rand", "rand_core", "redjubjub", "subtle", "tracing", + "visibility", "zcash_note_encryption", "zcash_spec", "zip32", @@ -5377,6 +5499,27 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "socket2" version = "0.5.8" @@ -5857,7 +6000,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2adb0fa957cad5a1f408e357a4450931366a9d35b78235d72260b6842518ba" dependencies = [ - "educe", + "educe 0.4.23", "futures", "oneshot-fused-workaround", "pin-project", @@ -5892,7 +6035,7 @@ dependencies = [ "bytes", "derive-deftly", "digest", - "educe", + "educe 0.4.23", "getrandom", "safelog", "thiserror 1.0.69", @@ -5911,7 +6054,7 @@ dependencies = [ "bytes", "caret", "derive_more", - "educe", + "educe 0.4.23", "paste", "rand", "smallvec", @@ -5949,7 +6092,7 @@ dependencies = [ "async-trait", "derive_builder_fork_arti", "derive_more", - "educe", + "educe 0.4.23", "futures", "oneshot-fused-workaround", "postage", @@ -5999,7 +6142,7 @@ dependencies = [ "derive_more", "downcast-rs", "dyn-clone", - "educe", + "educe 0.4.23", "futures", "humantime-serde", "itertools 0.13.0", @@ -6041,7 +6184,7 @@ dependencies = [ "derive-deftly", "derive_builder_fork_arti", "directories", - "educe", + "educe 0.4.23", "either", "figment", "fs-mistrust", @@ -6116,7 +6259,7 @@ dependencies = [ "derive_builder_fork_arti", "derive_more", "digest", - "educe", + "educe 0.4.23", "event-listener", "fs-mistrust", "fslock", @@ -6187,7 +6330,7 @@ dependencies = [ "derive_builder_fork_arti", "derive_more", "dyn-clone", - "educe", + "educe 0.4.23", "futures", "humantime", "humantime-serde", @@ -6332,7 +6475,7 @@ dependencies = [ "derive_more", "digest", "ed25519-dalek", - "educe", + "educe 0.4.23", "getrandom", "hex", "rand_core", @@ -6375,7 +6518,7 @@ checksum = "12551616f15f9d1fb4e37ee14ffc0b6743d0c31affbb4643374f456c0a820073" dependencies = [ "derive-deftly", "derive_more", - "educe", + "educe 0.4.23", "futures", "pin-project", "serde", @@ -6433,7 +6576,7 @@ dependencies = [ "derive_builder_fork_arti", "derive_more", "digest", - "educe", + "educe 0.4.23", "hex", "humantime", "itertools 0.13.0", @@ -6500,7 +6643,7 @@ dependencies = [ "derive_builder_fork_arti", "derive_more", "digest", - "educe", + "educe 0.4.23", "futures", "hkdf", "hmac", @@ -6567,7 +6710,7 @@ dependencies = [ "async_executors", "coarsetime", "derive_more", - "educe", + "educe 0.4.23", "futures", "futures-rustls", "paste", @@ -6592,7 +6735,7 @@ dependencies = [ "async-trait", "derive-deftly", "derive_more", - "educe", + "educe 0.4.23", "futures", "humantime", "itertools 0.13.0", @@ -7524,6 +7667,7 @@ dependencies = [ "bip0039", "bip32", "blake2b_simd", + "bytemuck", "chrono", "clap", "crossterm", @@ -7536,6 +7680,10 @@ dependencies = [ "iso_currency", "jubjub", "lazy_static", + "ledger-transport", + "ledger-transport-hid", + "ledger-zcash", + "ledger-zondax-generic", "minicbor", "nokhwa", "orchard", @@ -7544,6 +7692,7 @@ dependencies = [ "qrcode", "ratatui", "rayon", + "redjubjub", "roaring", "rqrr", "rusqlite", @@ -7578,6 +7727,7 @@ dependencies = [ "zcash_transparent", "zip32", "zip321", + "zx-bip44", ] [[package]] @@ -7719,6 +7869,7 @@ dependencies = [ "memuse", "nonempty", "orchard", + "proptest", "rand_core", "sapling-crypto", "secrecy 0.8.0", @@ -7969,3 +8120,13 @@ checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ "zune-core", ] + +[[package]] +name = "zx-bip44" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed17b569d1ea6903d5dc061602c6dc45f816cd0171d67d3b40fc1f6caf1ade0" +dependencies = [ + "byteorder", + "thiserror 1.0.69", +] diff --git a/Cargo.toml b/Cargo.toml index aa8a493..7a4bbb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,12 +29,12 @@ uuid = "1" orchard = { version = "0.10", default-features = false } pczt = "0.1" -sapling = { package = "sapling-crypto", version = "0.4" } +sapling = { package = "sapling-crypto", version = "0.4", features = ["test-dependencies", "unstable-ledger"] } transparent = { package = "zcash_transparent", version = "0.1", features = ["test-dependencies"] } zcash_address = "0.6" zcash_client_backend = { version = "0.16", features = ["lightwalletd-tonic-tls-webpki-roots", "orchard", "pczt", "tor"] } zcash_client_sqlite = { version = "0.14", features = ["unstable", "orchard", "serde"] } -zcash_keys = { version = "0.6", features = ["unstable", "orchard"] } +zcash_keys = { version = "0.6", features = ["unstable", "orchard", "test-dependencies"] } zcash_primitives = "0.21" zcash_proofs = "0.21" zcash_protocol = { version = "0.4", features = ["local-consensus"] } @@ -82,6 +82,18 @@ roaring = { version = "0.10", optional = true } tokio-util = { version = "0.7", optional = true } tui-logger = { version = "0.14", optional = true, features = ["tracing-support"] } +# Ledger Support +ledger-zcash = { git = "https://github.com/zecdev/ledger-zcash-rs.git", rev = "d12c85b7b2d12bd860cee55fc79cb46330259c56", optional = true } +zx-bip44 = { version = "0.1.0", optional = true } +redjubjub = { version = "0.7", optional = true } +ledger-transport = { version = "0.11", optional = true } +ledger-zondax-generic = { version = "0.11", optional = true } +ledger-transport-hid = { version = "0.11", optional = true } + +# Manage HidApi instance. +bytemuck = { version = "1.20.0", optional = true } + + [features] default = ["transparent-inputs"] pczt-qr = ["dep:image", "dep:minicbor", "dep:nokhwa", "dep:qrcode", "dep:rqrr", "dep:ur"] @@ -95,12 +107,22 @@ tui = [ "dep:tokio-util", "dep:tui-logger", ] +ledger-support = [ + "dep:ledger-zcash", + "dep:zx-bip44", + "dep:ledger-transport", + "dep:ledger-zondax-generic", + "dep:bytemuck", +] [patch.crates-io] +sapling-crypto = { git = "https://github.com/pacu/sapling-crypto.git", rev = "09320f2fb1d058185d5a739325e4b893b0012955" } +redjubjub = { git = "https://github.com/ZcashFoundation/redjubjub", rev = "1cf76ae8c02ec0b2627e20b60df526641d40c942" } + equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "4fa6d3b549f8803016a309281404eab095d04de8" } pczt = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } -sapling = { package = "sapling-crypto", git = "https://github.com/zcash/sapling-crypto.git", rev = "3c2235747553da642fb142d1eeb9b1afa8391987" } + transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } @@ -110,4 +132,4 @@ zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b9 zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } -zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } +zip321 = { git = "https://github.com/zcash/librustzcash.git", rev = "9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b" } \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2b9b1f1..8646cc3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "stable" -components = [ "clippy", "rustfmt" ] +channel = "1.81" +components = [ "clippy", "rustfmt", "rust-src" ] diff --git a/src/commands/wallet.rs b/src/commands/wallet.rs index 2fbdbaa..442299a 100644 --- a/src/commands/wallet.rs +++ b/src/commands/wallet.rs @@ -5,6 +5,8 @@ pub(crate) mod enhance; pub(crate) mod import_ufvk; pub(crate) mod init; pub(crate) mod init_fvk; +#[cfg(feature = "ledger-support")] +pub(crate) mod init_ledger; pub(crate) mod list_accounts; pub(crate) mod list_addresses; pub(crate) mod list_tx; @@ -24,6 +26,8 @@ pub(crate) enum Command { /// Initialise a new view-only light wallet InitFvk(init_fvk::Command), + #[cfg(feature = "ledger-support")] + InitLedger(init_ledger::Command), /// Reset an existing light wallet (does not preserve imported UFVKs) Reset(reset::Command), diff --git a/src/commands/wallet/init_ledger.rs b/src/commands/wallet/init_ledger.rs new file mode 100644 index 0000000..7f2d533 --- /dev/null +++ b/src/commands/wallet/init_ledger.rs @@ -0,0 +1,218 @@ +/// Initializes a wallet with a Ledger device. This assumes that HW wallet +/// is already containing a Seed Phrase, meaning that the user has gone to the +/// process of generating a new key or restoring it from existing ones using +/// the vendor's proposed workflow. +/// +/// A Ledger wallet can only contain a single set of seed bytes (known to the +/// user as a BIP-0039 mnemonic seed phrase) so this initialization will couple +/// any local data to the device ID for future uses so that users don't mix up +/// devices. On non-ledger workflows this is achieved by using the Seed's +/// Fingerprint but this is not accesible on the ledger code. +/// +/// The Ledger device can derive any number of accounts and addresses. This +/// command will initialize Account index Zero. User will be responsible of +/// deriving other account indices (this is the same behavior observed in +/// Ledger Live). +use anyhow::anyhow; +use clap::Args; +use bip32::{secp256k1::{elliptic_curve::PublicKey, Secp256k1}, PublicKeyBytes}; +use ledger_zcash::{app::ZcashApp, config::{DK_SIZE, FVK_SIZE}}; +use ledger_transport_hid::{hidapi::HidApi, TransportNativeHID}; +use sapling::{keys::FullViewingKey, zip32::{DiversifiableFullViewingKey, DiversifierKey}, Diversifier }; +use transparent::keys::AccountPubKey; +use zcash_client_backend::{ + data_api::{AccountPurpose, WalletWrite}, + proto::service, +}; +use zcash_keys::keys::UnifiedFullViewingKey; +use zcash_protocol::consensus; +use zip32::DiversifierIndex; +use zx_bip44::BIP44Path; + +use crate::{ + config::WalletConfig, + data::init_dbs, + remote::{tor_client, Servers}, +}; + +lazy_static::lazy_static! { + static ref HIDAPI: HidApi = HidApi::new().expect("Failed to create Hidapi"); +} + +// Options accepted for the `init_ledger` command +#[derive(Debug, Args)] +pub(crate) struct Command { + /// A name for the account + #[arg(long)] + name: String, + + /// The wallet's birthday (default is current chain height) + #[arg(long)] + birthday: Option, + + /// The server to initialize with (default is \"ecc\") + #[arg(short, long)] + #[arg(default_value = "ecc", value_parser = Servers::parse)] + server: Servers, + + /// Disable connections via TOR + #[arg(long)] + disable_tor: bool, +} + +impl Command { + pub(crate) async fn run(self, wallet_dir: Option) -> Result<(), anyhow::Error> { + let opts = self; + + // let (network_type, ufvk) = Ufvk::decode(&opts.fvk) + // .map_err(anyhow::Error::new) + // .and_then( + // |(network, ufvk)| -> Result<(NetworkType, UnifiedFullViewingKey), anyhow::Error> { + // let ufvk = UnifiedFullViewingKey::parse(&ufvk)?; + // Ok((network, ufvk)) + // }, + // ) + // .or_else( + // |_| -> Result<(NetworkType, UnifiedFullViewingKey), anyhow::Error> { + // let (network, sfvk) = decode_extfvk_with_network(&opts.fvk)?; + // let ufvk = UnifiedFullViewingKey::from_sapling_extended_full_viewing_key(sfvk)?; + // Ok((network, ufvk)) + // }, + // )?; + + // TODO: get network type from + let network = consensus::Network::MainNetwork; + // match network_type { + // NetworkType::Main => consensus::Network::MainNetwork, + // NetworkType::Test => consensus::Network::TestNetwork, + // NetworkType::Regtest => { + // return Err(anyhow!("the regtest network is not supported")); + // } + // }; + + // Connect to ledger and retrieve device id or fail + println!("Create Ledger App Transport"); + let app = ZcashApp::new(TransportNativeHID::new(&HIDAPI).expect("Unable to create transport")); + + println!("Connect to ledger and retrieve device version or fail"); + let version = app.get_version() + .await + .map_err(anyhow::Error::new)?; + + println!("version {:?}", version); + let ledger_id = app.get_device_info() + .await + .map_err(anyhow::Error::new)?; + + println!("Device ID {:?}", ledger_id); + + println!("get UnifiedFullViewingKey "); + // get UFVK + let mut ufvk_raw = app.get_ufvk(0) + .await + .map_err(anyhow::Error::new)?; + + + let dfvk = DiversifiableFullViewingKey::from_bytes(&ufvk_raw.dfvk) + .expect("Unable to create Sapling Diversifiable Full Viewing Key"); + + + let pub_key_bytes = PublicKeyBytes::from(ufvk_raw.transparent); + + let mut account_pub_key_bytes = [0u8; 65]; + + account_pub_key_bytes[32..].copy_from_slice(&pub_key_bytes); + + // Create the AccountPubKey for this UFVK + println!("Create the AccountPubKey for this UFVK"); + let account_pub_key = AccountPubKey::deserialize(&account_pub_key_bytes) + .map_err(anyhow::Error::new)?; + + let ufvk = UnifiedFullViewingKey::new( + Some(account_pub_key), + Some(dfvk), + None, + ) + .map_err(anyhow::Error::new)?; + + println!("Created UFVK: {:?}", ufvk); + + let server = opts.server.pick(network)?; + let mut client = if opts.disable_tor { + server.connect_direct().await? + } else { + server.connect(|| tor_client(wallet_dir.as_ref())).await? + }; + + // Get the current chain height (for the wallet's birthday recover-until height). + let chain_tip: u32 = client + .get_latest_block(service::ChainSpec::default()) + .await? + .into_inner() + .height + .try_into() + .expect("block heights must fit into u32"); + + let birthday = super::init::Command::get_wallet_birthday( + client, + opts.birthday + .unwrap_or(chain_tip.saturating_sub(100)) + .into(), + Some(chain_tip.into()), + ) + .await?; + + let purpose = AccountPurpose::ViewOnly; + + // Save the wallet config to disk. + WalletConfig::init_without_mnemonic(wallet_dir.as_ref(), birthday.height(), network)?; + + let mut wallet_db = init_dbs(network, wallet_dir.as_ref())?; + wallet_db.import_account_ufvk(&opts.name, &ufvk, &birthday, purpose, None)?; + + Ok(()) + } + + /// Retrieve the connected ledger's "ID" + /// + /// Uses 44'/1'/0/0/0 derivation path + /// Note: This is TBD + async fn get_id(app: &ZcashApp) -> Result, anyhow::Error> { + let addr = app.get_address_unshielded( + &BIP44Path([44 + 0x8000_0000, 1 + 0x8000_0000, 0, 0, 0]), + false, + ) + .await + .map_err(|_| anyhow!("Failed to get unshielded address for \"ID\""))?; + + let pub_key = PublicKey::from_sec1_bytes(&addr.public_key) + .map_err(|_| anyhow!("Failed to generate \"ID\" from public key"))?; + + Ok(pub_key) + } + + /// Retrieve the defualt diversifier from a given device and path + /// + /// The defualt diversifier is the first valid diversifier starting + /// from index 0 + async fn get_default_div_from(app: &ZcashApp, idx: u32) -> Result { + let mut index = DiversifierIndex::new(); + + loop { + let divs = app.get_div_list(idx, index.as_bytes()).await?; + let divs: &[[u8; 11]] = bytemuck::cast_slice(&divs); + + //find the first div that is not all 0s + // all 0s is when it's an invalid diversifier + for div in divs { + if div != &[0; 11] { + return Ok(Diversifier(*div)); + } + + //increment the index for each diversifier returned + index.increment().map_err(|_| anyhow!("Diversifier Overflow"))?; + } + } + } +} + diff --git a/src/commands/wallet/list_addresses.rs b/src/commands/wallet/list_addresses.rs index 35b9755..241f62d 100644 --- a/src/commands/wallet/list_addresses.rs +++ b/src/commands/wallet/list_addresses.rs @@ -22,9 +22,14 @@ impl Command { let account = select_account(&db_data, self.account_id)?; println!("Account {:?}", account.id()); + #[cfg(not(feature = "ledger-support"))] let (ua, _) = account .uivk() .default_address(UnifiedAddressRequest::all())?; + #[cfg(feature = "ledger-support")] + let (ua, _) = account + .uivk() + .default_address(UnifiedAddressRequest::new(false, true, true))?; println!(" Default Address: {}", ua.encode(¶ms)); Ok(()) } diff --git a/src/main.rs b/src/main.rs index ee97823..c0bf106 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,6 +124,8 @@ fn main() -> Result<(), anyhow::Error> { })) => match command { commands::wallet::Command::Init(command) => command.run(wallet_dir).await, commands::wallet::Command::InitFvk(command) => command.run(wallet_dir).await, + #[cfg(feature = "ledger-support")] + commands::wallet::Command::InitLedger(command) => command.run(wallet_dir).await, commands::wallet::Command::Reset(command) => command.run(wallet_dir).await, commands::wallet::Command::ImportUfvk(command) => command.run(wallet_dir).await, commands::wallet::Command::Upgrade(command) => command.run(wallet_dir),