diff --git a/Cargo.lock b/Cargo.lock index 9657010b5b..ac0f81a1f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,6 +129,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" @@ -148,18 +154,17 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", "http", "http-body", - "hyper", + "http-body-util", "itoa", "matchit", "memchr", @@ -168,7 +173,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower", "tower-layer", "tower-service", @@ -176,17 +181,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", "http", "http-body", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] @@ -212,6 +220,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.0.1" @@ -247,9 +261,9 @@ dependencies = [ [[package]] name = "bip0039" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef0f0152ec5cf17f49a5866afaa3439816207fd4f0a224c0211ffaf5e278426" +checksum = "e68a5a99c65851e7be249f5cf510c0a136f18c9bca32139576d59bd3f577b043" dependencies = [ "hmac", "pbkdf2", @@ -372,9 +386,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cast" @@ -868,17 +882,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -958,9 +972,8 @@ dependencies = [ [[package]] name = "hdwallet" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7" +version = "0.4.2" +source = "git+https://github.com/fluidvanadium/hdwallet.git?tag=update_secp256k1_to_0.27.0#fca2d8f5d93bfd4801a1ec5ba96f7a02d4c12eb7" dependencies = [ "lazy_static", "rand_core", @@ -1016,9 +1029,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1027,12 +1040,24 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", "pin-project-lite", ] @@ -1050,13 +1075,12 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", @@ -1065,30 +1089,49 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", "hyper", "pin-project-lite", + "socket2", "tokio", - "tokio-io-timeout", + "tower", + "tower-service", + "tracing", ] [[package]] name = "incrementalmerkletree" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1872810fb725b06b8c153dde9e86f3ec26747b9b60096da7a869883b549cbe" +checksum = "75346da3bd8e3d8891d02508245ed2df34447ca6637e343829f8d08986e9cde2" dependencies = [ "either", "proptest", @@ -1477,9 +1520,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0462569fc8b0d1b158e4d640571867a4e4319225ebee2ab6647e60c70af19ae3" +checksum = "4dc7bde644aeb980be296cd908c6650894dc8541deb56f9f5294c52ed7ca568f" dependencies = [ "aes", "bitvec", @@ -1501,6 +1544,7 @@ dependencies = [ "serde", "subtle", "tracing", + "visibility", "zcash_note_encryption", "zcash_spec", "zip32", @@ -1546,9 +1590,9 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.3.2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", "rand_core", @@ -1572,9 +1616,9 @@ dependencies = [ [[package]] name = "pbkdf2" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest", "password-hash", @@ -1760,6 +1804,15 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +dependencies = [ + "bytes", +] + [[package]] name = "prost-build" version = "0.12.1" @@ -1774,7 +1827,7 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", + "prost 0.12.1", "prost-types", "regex", "syn 2.0.39", @@ -1801,7 +1854,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" dependencies = [ - "prost", + "prost 0.12.1", ] [[package]] @@ -2106,9 +2159,9 @@ dependencies = [ [[package]] name = "sapling-crypto" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f4270033afcb0c74c5c7d59c73cfd1040367f67f224fe7ed9a919ae618f1b7" +checksum = "15e379398fffad84e49f9a45a05635fc004f66086e65942dbf4eb95332c26d2a" dependencies = [ "aes", "bellman", @@ -2178,9 +2231,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ "secp256k1-sys", ] @@ -2247,9 +2300,9 @@ dependencies = [ [[package]] name = "shardtree" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d766257c56a1bdd75479c256b97c92e72788a9afb18b5199f58faf7188dc99d9" +checksum = "78222845cd8bbe5eb95687407648ff17693a35de5e8abaa39a4681fb21e033f9" dependencies = [ "assert_matches", "bitflags 2.4.1", @@ -2270,19 +2323,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -2381,6 +2424,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "tap" version = "1.0.1" @@ -2489,18 +2538,20 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2 0.5.5", + "socket2", + "tokio-macros", "windows-sys", ] [[package]] -name = "tokio-io-timeout" -version = "1.2.0" +name = "tokio-macros" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "pin-project-lite", - "tokio", + "proc-macro2", + "quote", + "syn 2.0.39", ] [[package]] @@ -2530,23 +2581,26 @@ dependencies = [ [[package]] name = "tonic" -version = "0.10.2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", "axum", - "base64", + "base64 0.22.1", "bytes", "h2", "http", "http-body", + "http-body-util", "hyper", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", - "prost", + "prost 0.13.2", + "socket2", "tokio", "tokio-stream", "tower", @@ -2716,6 +2770,17 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "wagyu-zcash-parameters" version = "0.2.0" @@ -3012,7 +3077,7 @@ name = "zcash_client_backend" version = "0.12.1" dependencies = [ "assert_matches", - "base64", + "base64 0.21.5", "bech32", "bls12_381", "bs58", @@ -3031,7 +3096,7 @@ dependencies = [ "orchard", "percent-encoding", "proptest", - "prost", + "prost 0.12.1", "rand_core", "rayon", "sapling-crypto", @@ -3071,7 +3136,7 @@ dependencies = [ "orchard", "pasta_curves", "proptest", - "prost", + "prost 0.12.1", "rand_chacha", "rand_core", "regex", diff --git a/Cargo.toml b/Cargo.toml index c170761825..00a9e11013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ [workspace.package] edition = "2021" -rust-version = "1.65" +rust-version = "1.77.0" repository = "https://github.com/zcash/librustzcash" license = "MIT OR Apache-2.0" categories = ["cryptography::cryptocurrencies"] @@ -42,8 +42,8 @@ zcash_proofs = { version = "0.15", path = "zcash_proofs", default-features = fal # Shielded protocols ff = "0.13" group = "0.13" -incrementalmerkletree = "0.5.1" -shardtree = "0.3" +incrementalmerkletree = "0.6.0" +shardtree = "0.4" zcash_spec = "0.1" # Payment protocols @@ -52,17 +52,17 @@ bitvec = "1" blake2s_simd = "1" bls12_381 = "0.8" jubjub = "0.10" -sapling = { package = "sapling-crypto", version = "0.1.3" } +sapling = { package = "sapling-crypto", version = "0.2" } # - Orchard nonempty = "0.7" -orchard = { version = "0.8.0", default-features = false } +orchard = { version = "0.9", default-features = false } pasta_curves = "0.5" # - Transparent -hdwallet = "0.4" +hdwallet = { git = "https://github.com/fluidvanadium/hdwallet.git", tag = "update_secp256k1_to_0.27.0" } ripemd = "0.1" -secp256k1 = "0.26" +secp256k1 = "0.27" # CSPRNG rand = "0.8" @@ -93,13 +93,13 @@ maybe-rayon = { version = "0.1.0", default-features = false } rayon = "1.5" # Protobuf and gRPC -prost = "0.12" -tonic = { version = "0.10", default-features = false } -tonic-build = { version = "0.10", default-features = false } +prost = "0.13" +tonic = { version = "0.12", default-features = false } +tonic-build = { version = "0.12", default-features = false } # Secret management secrecy = "0.8" -subtle = "2.2.3" +subtle = "2.4.1" # Static constants lazy_static = "1" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5ecda6e495..0314e2f5e4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.65.0" +channel = "1.77.0" components = [ "clippy", "rustfmt" ] diff --git a/zcash_client_backend/CHANGELOG.md b/zcash_client_backend/CHANGELOG.md index 009c79ea2b..c776acbcd2 100644 --- a/zcash_client_backend/CHANGELOG.md +++ b/zcash_client_backend/CHANGELOG.md @@ -5,7 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Zingo] +- replace create_proposed_transaction with `calculate_proposed_transaction` + - dont try to write the calculated transaction to wallet, that will be handled later + - ignore collecting any transaction metadata + - handle usk differently +- modify change algorithm +- added display for NoteId ## [0.12.1] - 2024-03-27 diff --git a/zcash_client_backend/build.rs b/zcash_client_backend/build.rs index 396738bdb5..6ea7d5461c 100644 --- a/zcash_client_backend/build.rs +++ b/zcash_client_backend/build.rs @@ -43,7 +43,7 @@ fn build() -> io::Result<()> { // Build the gRPC types and client. tonic_build::configure() - .build_server(false) + .build_server(true) .client_mod_attribute( "cash.z.wallet.sdk.rpc", r#"#[cfg(feature = "lightwalletd-tonic")]"#, diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 922d2544c1..611b0c26ba 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -52,7 +52,7 @@ use crate::{ fees::{self, DustOutputPolicy}, keys::UnifiedSpendingKey, proposal::{self, Proposal, ProposalError}, - wallet::{Note, OvkPolicy, Recipient}, + wallet::{Note, OvkPolicy, Recipient, TransparentAddressMetadata}, zip321::{self, Payment}, PoolType, ShieldedProtocol, }; @@ -605,9 +605,10 @@ where ParamsT: consensus::Parameters + Clone, FeeRuleT: FeeRule, { + unimplemented!(); let mut step_results = Vec::with_capacity(proposal.steps().len()); for step in proposal.steps() { - let step_result = create_proposed_transaction( + let step_result = calculate_proposed_transaction( wallet_db, params, spend_prover, @@ -618,6 +619,8 @@ where proposal.min_target_height(), &step_results, step, + None, + None, )?; step_results.push((step, step_result)); } @@ -631,9 +634,10 @@ where .expect("proposal.steps is NonEmpty")) } +/// Zingo uses calculate_proposed_transaction to create the transaction, and then stores it ASYNCRONOUSLY #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] -fn create_proposed_transaction( +pub fn calculate_proposed_transaction( wallet_db: &mut DbT, params: &ParamsT, spend_prover: &impl SpendProver, @@ -644,6 +648,10 @@ fn create_proposed_transaction( min_target_height: BlockHeight, prior_step_results: &[(&proposal::Step, BuildResult)], proposal_step: &proposal::Step, + usk_to_tkey: Option< + fn(&UnifiedSpendingKey, &TransparentAddressMetadata) -> hdwallet::secp256k1::SecretKey, + >, + override_sapling_change_address: Option, ) -> Result< BuildResult, Error< @@ -654,7 +662,7 @@ fn create_proposed_transaction( >, > where - DbT: WalletWrite + WalletCommitmentTrees, + DbT: WalletRead + WalletCommitmentTrees, ParamsT: consensus::Parameters + Clone, FeeRuleT: FeeRule, { @@ -822,10 +830,16 @@ where .clone() .ok_or_else(|| Error::NoSpendingKey(addr.encode(params)))?; - let secret_key = usk - .transparent() - .derive_secret_key(address_metadata.scope(), address_metadata.address_index()) - .unwrap(); + let secret_key = usk_to_tkey + .map(|f| f(usk, &address_metadata)) + .unwrap_or_else(|| { + usk.transparent() + .derive_secret_key( + address_metadata.scope(), + address_metadata.address_index(), + ) + .unwrap() + }); utxos_spent.push(outpoint.clone()); builder.add_transparent_input(secret_key, outpoint, utxo)?; @@ -935,10 +949,6 @@ where Some(sapling_dfvk.to_ovk(Scope::Internal)) }; - #[cfg(feature = "orchard")] - let mut orchard_output_meta = vec![]; - let mut sapling_output_meta = vec![]; - let mut transparent_output_meta = vec![]; for (payment, output_pool) in proposal_step .payment_pools() .iter() @@ -973,14 +983,6 @@ where payment.amount.into(), memo.clone(), )?; - orchard_output_meta.push(( - Recipient::Unified( - ua.clone(), - PoolType::Shielded(ShieldedProtocol::Orchard), - ), - payment.amount, - Some(memo), - )); } PoolType::Shielded(ShieldedProtocol::Sapling) => { @@ -990,14 +992,6 @@ where payment.amount, memo.clone(), )?; - sapling_output_meta.push(( - Recipient::Unified( - ua.clone(), - PoolType::Shielded(ShieldedProtocol::Sapling), - ), - payment.amount, - Some(memo), - )); } PoolType::Transparent => { @@ -1023,7 +1017,6 @@ where payment.amount, memo.clone(), )?; - sapling_output_meta.push((Recipient::Sapling(*addr), payment.amount, Some(memo))); } Address::Transparent(to) => { if payment.memo.is_some() { @@ -1031,7 +1024,6 @@ where } else { builder.add_transparent_output(to, payment.amount)?; } - transparent_output_meta.push((to, payment.amount)); } } } @@ -1044,19 +1036,10 @@ where ShieldedProtocol::Sapling => { builder.add_sapling_output( sapling_internal_ovk(), - sapling_dfvk.change_address().1, + override_sapling_change_address.unwrap_or(sapling_dfvk.change_address().1), change_value.value(), memo.clone(), )?; - sapling_output_meta.push(( - Recipient::InternalAccount { - receiving_account: account, - external_address: None, - note: PoolType::Shielded(ShieldedProtocol::Sapling), - }, - change_value.value(), - Some(memo), - )) } ShieldedProtocol::Orchard => { #[cfg(not(feature = "orchard"))] @@ -1068,19 +1051,10 @@ where { builder.add_orchard_output( orchard_internal_ovk(), - orchard_fvk.address_at(0u32, orchard::keys::Scope::Internal), + orchard_fvk.address_at(0u32, orchard::keys::Scope::External), change_value.value().into(), memo.clone(), )?; - orchard_output_meta.push(( - Recipient::InternalAccount { - receiving_account: account, - external_address: None, - note: PoolType::Shielded(ShieldedProtocol::Orchard), - }, - change_value.value(), - Some(memo), - )) } } } @@ -1089,105 +1063,6 @@ where // Build the transaction with the specified fee rule let build_result = builder.build(OsRng, spend_prover, output_prover, fee_rule)?; - #[cfg(feature = "orchard")] - let orchard_internal_ivk = orchard_fvk.to_ivk(orchard::keys::Scope::Internal); - #[cfg(feature = "orchard")] - let orchard_outputs = - orchard_output_meta - .into_iter() - .enumerate() - .map(|(i, (recipient, value, memo))| { - let output_index = build_result - .orchard_meta() - .output_action_index(i) - .expect("An action should exist in the transaction for each Orchard output."); - - let recipient = recipient - .map_internal_account_note(|pool| { - assert!(pool == PoolType::Shielded(ShieldedProtocol::Orchard)); - build_result - .transaction() - .orchard_bundle() - .and_then(|bundle| { - bundle - .decrypt_output_with_key(output_index, &orchard_internal_ivk) - .map(|(note, _, _)| Note::Orchard(note)) - }) - }) - .internal_account_note_transpose_option() - .expect("Wallet-internal outputs must be decryptable with the wallet's IVK"); - - SentTransactionOutput::from_parts(output_index, recipient, value, memo) - }); - - let sapling_internal_ivk = - PreparedIncomingViewingKey::new(&sapling_dfvk.to_ivk(Scope::Internal)); - let sapling_outputs = - sapling_output_meta - .into_iter() - .enumerate() - .map(|(i, (recipient, value, memo))| { - let output_index = build_result - .sapling_meta() - .output_index(i) - .expect("An output should exist in the transaction for each Sapling payment."); - - let recipient = recipient - .map_internal_account_note(|pool| { - assert!(pool == PoolType::Shielded(ShieldedProtocol::Sapling)); - build_result - .transaction() - .sapling_bundle() - .and_then(|bundle| { - try_sapling_note_decryption( - &sapling_internal_ivk, - &bundle.shielded_outputs()[output_index], - zip212_enforcement(params, min_target_height), - ) - .map(|(note, _, _)| Note::Sapling(note)) - }) - }) - .internal_account_note_transpose_option() - .expect("Wallet-internal outputs must be decryptable with the wallet's IVK"); - - SentTransactionOutput::from_parts(output_index, recipient, value, memo) - }); - - let transparent_outputs = transparent_output_meta.into_iter().map(|(addr, value)| { - let script = addr.script(); - let output_index = build_result - .transaction() - .transparent_bundle() - .and_then(|b| { - b.vout - .iter() - .enumerate() - .find(|(_, tx_out)| tx_out.script_pubkey == script) - }) - .map(|(index, _)| index) - .expect("An output should exist in the transaction for each transparent payment."); - - SentTransactionOutput::from_parts(output_index, Recipient::Transparent(*addr), value, None) - }); - - let mut outputs = vec![]; - #[cfg(feature = "orchard")] - outputs.extend(orchard_outputs); - outputs.extend(sapling_outputs); - outputs.extend(transparent_outputs); - - wallet_db - .store_sent_tx(&SentTransaction { - tx: build_result.transaction(), - created: time::OffsetDateTime::now_utc(), - account, - outputs, - fee_amount: proposal_step.balance().fee_required(), - #[cfg(feature = "transparent-inputs")] - utxos_spent, - }) - .map_err(Error::DataSource)?; - Ok(build_result) } diff --git a/zcash_client_backend/src/fees/common.rs b/zcash_client_backend/src/fees/common.rs index a8a791a527..2ee0ca4a76 100644 --- a/zcash_client_backend/src/fees/common.rs +++ b/zcash_client_backend/src/fees/common.rs @@ -202,40 +202,36 @@ where required: total_out, })?; - if proposed_change.is_zero() { - TransactionBalance::new(vec![], fee_amount).map_err(|_| overflow()) - } else { - let dust_threshold = dust_output_policy - .dust_threshold() - .unwrap_or(default_dust_threshold); - - if proposed_change < dust_threshold { - match dust_output_policy.action() { - DustAction::Reject => { - let shortfall = (dust_threshold - proposed_change).ok_or_else(underflow)?; - - Err(ChangeError::InsufficientFunds { - available: total_in, - required: (total_in + shortfall).ok_or_else(overflow)?, - }) - } - DustAction::AllowDustChange => TransactionBalance::new( - vec![ChangeValue::new(change_pool, proposed_change, change_memo)], - fee_amount, - ) - .map_err(|_| overflow()), - DustAction::AddDustToFee => TransactionBalance::new( - vec![], - (fee_amount + proposed_change).ok_or_else(overflow)?, - ) - .map_err(|_| overflow()), + let dust_threshold = dust_output_policy + .dust_threshold() + .unwrap_or(default_dust_threshold); + + if proposed_change < dust_threshold { + match dust_output_policy.action() { + DustAction::Reject => { + let shortfall = (dust_threshold - proposed_change).ok_or_else(underflow)?; + + Err(ChangeError::InsufficientFunds { + available: total_in, + required: (total_in + shortfall).ok_or_else(overflow)?, + }) } - } else { - TransactionBalance::new( + DustAction::AllowDustChange => TransactionBalance::new( vec![ChangeValue::new(change_pool, proposed_change, change_memo)], fee_amount, ) - .map_err(|_| overflow()) + .map_err(|_| overflow()), + DustAction::AddDustToFee => TransactionBalance::new( + vec![], + (fee_amount + proposed_change).ok_or_else(overflow)?, + ) + .map_err(|_| overflow()), } + } else { + TransactionBalance::new( + vec![ChangeValue::new(change_pool, proposed_change, change_memo)], + fee_amount, + ) + .map_err(|_| overflow()) } } diff --git a/zcash_client_backend/src/proto/service.rs b/zcash_client_backend/src/proto/service.rs index ccfd3fed7a..141e6b241f 100644 --- a/zcash_client_backend/src/proto/service.rs +++ b/zcash_client_backend/src/proto/service.rs @@ -924,3 +924,1233 @@ pub mod compact_tx_streamer_client { } } } +/// Generated server implementations. +pub mod compact_tx_streamer_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with CompactTxStreamerServer. + #[async_trait] + pub trait CompactTxStreamer: Send + Sync + 'static { + /// Return the height of the tip of the best chain + async fn get_latest_block( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Return the compact block corresponding to the given block identifier + async fn get_block( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Same as GetBlock except actions contain only nullifiers + async fn get_block_nullifiers( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the GetBlockRange method. + type GetBlockRangeStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + crate::proto::compact_formats::CompactBlock, + tonic::Status, + >, + > + + Send + + 'static; + /// Return a list of consecutive compact blocks + async fn get_block_range( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the GetBlockRangeNullifiers method. + type GetBlockRangeNullifiersStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + crate::proto::compact_formats::CompactBlock, + tonic::Status, + >, + > + + Send + + 'static; + /// Same as GetBlockRange except actions contain only nullifiers + async fn get_block_range_nullifiers( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Return the requested full (not compact) transaction (as from zcashd) + async fn get_transaction( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Submit the given transaction to the Zcash network + async fn send_transaction( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the GetTaddressTxids method. + type GetTaddressTxidsStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + /// Return the txids corresponding to the given t-address within the given block range + async fn get_taddress_txids( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn get_taddress_balance( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + async fn get_taddress_balance_stream( + &self, + request: tonic::Request>, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the GetMempoolTx method. + type GetMempoolTxStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + crate::proto::compact_formats::CompactTx, + tonic::Status, + >, + > + + Send + + 'static; + /// Return the compact transactions currently in the mempool; the results + /// can be a few seconds out of date. If the Exclude list is empty, return + /// all transactions; otherwise return all *except* those in the Exclude list + /// (if any); this allows the client to avoid receiving transactions that it + /// already has (from an earlier call to this rpc). The transaction IDs in the + /// Exclude list can be shortened to any number of bytes to make the request + /// more bandwidth-efficient; if two or more transactions in the mempool + /// match a shortened txid, they are all sent (none is excluded). Transactions + /// in the exclude list that don't exist in the mempool are ignored. + async fn get_mempool_tx( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the GetMempoolStream method. + type GetMempoolStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + /// Return a stream of current Mempool transactions. This will keep the output stream open while + /// there are mempool transactions. It will close the returned stream when a new block is mined. + async fn get_mempool_stream( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// GetTreeState returns the note commitment tree state corresponding to the given block. + /// See section 3.7 of the Zcash protocol specification. It returns several other useful + /// values also (even though they can be obtained using GetBlock). + /// The block can be specified by either height or hash. + async fn get_tree_state( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + async fn get_latest_tree_state( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the GetSubtreeRoots method. + type GetSubtreeRootsStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + /// Returns a stream of information about roots of subtrees of the Sapling and Orchard + /// note commitment trees. + async fn get_subtree_roots( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn get_address_utxos( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the GetAddressUtxosStream method. + type GetAddressUtxosStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + async fn get_address_utxos_stream( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Return information about this lightwalletd instance and the blockchain + async fn get_lightd_info( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Testing-only, requires lightwalletd --ping-very-insecure (do not enable in production) + async fn ping( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + } + #[derive(Debug)] + pub struct CompactTxStreamerServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl CompactTxStreamerServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for CompactTxStreamerServer + where + T: CompactTxStreamer, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLatestBlock" => { + #[allow(non_camel_case_types)] + struct GetLatestBlockSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for GetLatestBlockSvc { + type Response = super::BlockId; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_latest_block(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetLatestBlockSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlock" => { + #[allow(non_camel_case_types)] + struct GetBlockSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService for GetBlockSvc { + type Response = crate::proto::compact_formats::CompactBlock; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlockNullifiers" => { + #[allow(non_camel_case_types)] + struct GetBlockNullifiersSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for GetBlockNullifiersSvc { + type Response = crate::proto::compact_formats::CompactBlock; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block_nullifiers( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockNullifiersSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlockRange" => { + #[allow(non_camel_case_types)] + struct GetBlockRangeSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService + for GetBlockRangeSvc { + type Response = crate::proto::compact_formats::CompactBlock; + type ResponseStream = T::GetBlockRangeStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block_range(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockRangeSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlockRangeNullifiers" => { + #[allow(non_camel_case_types)] + struct GetBlockRangeNullifiersSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService + for GetBlockRangeNullifiersSvc { + type Response = crate::proto::compact_formats::CompactBlock; + type ResponseStream = T::GetBlockRangeNullifiersStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block_range_nullifiers( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockRangeNullifiersSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTransaction" => { + #[allow(non_camel_case_types)] + struct GetTransactionSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for GetTransactionSvc { + type Response = super::RawTransaction; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_transaction(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTransactionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/SendTransaction" => { + #[allow(non_camel_case_types)] + struct SendTransactionSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for SendTransactionSvc { + type Response = super::SendResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::send_transaction(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SendTransactionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressTxids" => { + #[allow(non_camel_case_types)] + struct GetTaddressTxidsSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService< + super::TransparentAddressBlockFilter, + > for GetTaddressTxidsSvc { + type Response = super::RawTransaction; + type ResponseStream = T::GetTaddressTxidsStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_taddress_txids( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTaddressTxidsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalance" => { + #[allow(non_camel_case_types)] + struct GetTaddressBalanceSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for GetTaddressBalanceSvc { + type Response = super::Balance; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_taddress_balance( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTaddressBalanceSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalanceStream" => { + #[allow(non_camel_case_types)] + struct GetTaddressBalanceStreamSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ClientStreamingService + for GetTaddressBalanceStreamSvc { + type Response = super::Balance; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request>, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_taddress_balance_stream( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTaddressBalanceStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.client_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolTx" => { + #[allow(non_camel_case_types)] + struct GetMempoolTxSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService + for GetMempoolTxSvc { + type Response = crate::proto::compact_formats::CompactTx; + type ResponseStream = T::GetMempoolTxStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_mempool_tx(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetMempoolTxSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolStream" => { + #[allow(non_camel_case_types)] + struct GetMempoolStreamSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService + for GetMempoolStreamSvc { + type Response = super::RawTransaction; + type ResponseStream = T::GetMempoolStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_mempool_stream( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetMempoolStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTreeState" => { + #[allow(non_camel_case_types)] + struct GetTreeStateSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for GetTreeStateSvc { + type Response = super::TreeState; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_tree_state(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTreeStateSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLatestTreeState" => { + #[allow(non_camel_case_types)] + struct GetLatestTreeStateSvc(pub Arc); + impl tonic::server::UnaryService + for GetLatestTreeStateSvc { + type Response = super::TreeState; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_latest_tree_state( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetLatestTreeStateSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetSubtreeRoots" => { + #[allow(non_camel_case_types)] + struct GetSubtreeRootsSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService + for GetSubtreeRootsSvc { + type Response = super::SubtreeRoot; + type ResponseStream = T::GetSubtreeRootsStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_subtree_roots(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetSubtreeRootsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxos" => { + #[allow(non_camel_case_types)] + struct GetAddressUtxosSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService + for GetAddressUtxosSvc { + type Response = super::GetAddressUtxosReplyList; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_address_utxos(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetAddressUtxosSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream" => { + #[allow(non_camel_case_types)] + struct GetAddressUtxosStreamSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::ServerStreamingService + for GetAddressUtxosStreamSvc { + type Response = super::GetAddressUtxosReply; + type ResponseStream = T::GetAddressUtxosStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_address_utxos_stream( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetAddressUtxosStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLightdInfo" => { + #[allow(non_camel_case_types)] + struct GetLightdInfoSvc(pub Arc); + impl tonic::server::UnaryService + for GetLightdInfoSvc { + type Response = super::LightdInfo; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_lightd_info(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetLightdInfoSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/cash.z.wallet.sdk.rpc.CompactTxStreamer/Ping" => { + #[allow(non_camel_case_types)] + struct PingSvc(pub Arc); + impl< + T: CompactTxStreamer, + > tonic::server::UnaryService for PingSvc { + type Response = super::PingResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::ping(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = PingSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for CompactTxStreamerServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService + for CompactTxStreamerServer { + const NAME: &'static str = "cash.z.wallet.sdk.rpc.CompactTxStreamer"; + } +} diff --git a/zcash_client_backend/src/scanning.rs b/zcash_client_backend/src/scanning.rs index 78b5852839..3c63dcdcb3 100644 --- a/zcash_client_backend/src/scanning.rs +++ b/zcash_client_backend/src/scanning.rs @@ -5,7 +5,7 @@ use std::convert::TryFrom; use std::fmt::{self, Debug}; use std::hash::Hash; -use incrementalmerkletree::{Position, Retention}; +use incrementalmerkletree::{Marking, Position, Retention}; use sapling::{ note_encryption::{CompactOutputDescription, SaplingDomain}, SaplingIvk, @@ -1105,7 +1105,11 @@ fn find_received< let retention = match (decrypted_note.is_some(), is_checkpoint) { (is_marked, true) => Retention::Checkpoint { id: block_height, - is_marked, + marking: if is_marked { + Marking::Marked + } else { + Marking::None + }, }, (true, false) => Retention::Marked, (false, false) => Retention::Ephemeral, diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index 418bb3e3a1..a822b5e829 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -1,6 +1,8 @@ //! Structs representing transaction data scanned from the block chain by a wallet or //! light client. +use std::fmt; + use incrementalmerkletree::Position; use zcash_keys::address::Address; use zcash_note_encryption::EphemeralKeyBytes; @@ -62,6 +64,18 @@ impl NoteId { } } +impl fmt::Display for NoteId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "txid {} protocol {:?} output_index {}", + self.txid(), + self.protocol(), + self.output_index() + ) + } +} + /// A type that represents the recipient of a transaction output: a recipient address (and, for /// unified addresses, the pool to which the payment is sent) in the case of an outgoing output, or an /// internal account ID and the pool to which funds were sent in the case of a wallet-internal diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index b8f24b84cf..6422b1b13b 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -64,7 +64,7 @@ hdwallet = { workspace = true, optional = true } secp256k1 = { workspace = true, optional = true } # - ZIP 339 -bip0039 = { version = "0.10", features = ["std", "all-languages"] } +bip0039 = { version = "0.11", features = ["std", "all-languages"] } # Dependencies used internally: # (Breaking upgrades to these are usually backwards-compatible, but check MSRVs.)