From 32f87fab419698476ab8c4cb60b5c52749286028 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 6 Dec 2024 09:33:26 -0700 Subject: [PATCH] Add `createProposedTransactionPczt` to the Rust backend. --- backend-lib/Cargo.lock | 173 ++++++++++++++++++++++++++++--- backend-lib/Cargo.toml | 16 +-- backend-lib/src/main/rust/lib.rs | 101 +++++++++++++++++- 3 files changed, 265 insertions(+), 25 deletions(-) diff --git a/backend-lib/Cargo.lock b/backend-lib/Cargo.lock index c1607f0ff..5ce3533ac 100644 --- a/backend-lib/Cargo.lock +++ b/backend-lib/Cargo.lock @@ -257,6 +257,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -561,6 +570,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "combine" version = "4.6.7" @@ -625,6 +640,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -1114,6 +1135,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "enum-ordinalize" version = "3.1.15" @@ -1130,7 +1163,7 @@ dependencies = [ [[package]] name = "equihash" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "blake2b_simd", "byteorder", @@ -1166,7 +1199,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "blake2b_simd", ] @@ -1429,6 +1462,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getset" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1493,6 +1538,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1523,6 +1577,20 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -2248,14 +2316,14 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orchard" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f18e997fa121de5c73e95cdc7e8512ae43b7de38904aeea5e5713cc48f3c0ba" +source = "git+https://github.com/zcash/orchard.git?rev=bcd08e1d23e70c42a338f3e3f79d6f4c0c219805#bcd08e1d23e70c42a338f3e3f79d6f4c0c219805" dependencies = [ "aes", "bitvec", "blake2b_simd", "ff", "fpe", + "getset", "group", "halo2_gadgets", "halo2_proofs", @@ -2402,6 +2470,31 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pczt" +version = "0.0.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" +dependencies = [ + "blake2b_simd", + "bls12_381", + "ff", + "getset", + "jubjub", + "nonempty", + "orchard", + "pasta_curves", + "postcard", + "rand_core", + "redjubjub", + "sapling-crypto", + "secp256k1", + "serde", + "serde_with", + "zcash_note_encryption", + "zcash_primitives", + "zcash_protocol", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2554,6 +2647,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "postcard" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2608,6 +2714,28 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "proc-macro2" version = "1.0.89" @@ -3074,8 +3202,7 @@ dependencies = [ [[package]] name = "sapling-crypto" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfff8cfce16aeb38da50b8e2ed33c9018f30552beff2210c266662a021b17f38" +source = "git+https://github.com/zcash/sapling-crypto.git?rev=42a1de5a20007050fd3169b5da1cc28962dcb258#42a1de5a20007050fd3169b5da1cc28962dcb258" dependencies = [ "aes", "bellman", @@ -3087,6 +3214,7 @@ dependencies = [ "document-features", "ff", "fpe", + "getset", "group", "hex", "incrementalmerkletree", @@ -3430,6 +3558,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -3482,6 +3613,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -5237,11 +5374,12 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.6.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "bech32", "bs58", "f4jumble", + "serde", "zcash_encoding", "zcash_protocol", ] @@ -5249,7 +5387,7 @@ dependencies = [ [[package]] name = "zcash_client_backend" version = "0.15.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "arti-client", "base64", @@ -5273,7 +5411,9 @@ dependencies = [ "nonempty", "orchard", "pasta_curves", + "pczt", "percent-encoding", + "postcard", "prost", "rand", "rand_core", @@ -5308,7 +5448,7 @@ dependencies = [ [[package]] name = "zcash_client_sqlite" version = "0.13.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "bip32", "bs58", @@ -5345,7 +5485,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.2.1" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "byteorder", "nonempty", @@ -5354,7 +5494,7 @@ dependencies = [ [[package]] name = "zcash_keys" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "bech32", "bip32", @@ -5395,7 +5535,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.20.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "aes", "bip32", @@ -5406,6 +5546,7 @@ dependencies = [ "equihash", "ff", "fpe", + "getset", "group", "hex", "incrementalmerkletree", @@ -5419,6 +5560,7 @@ dependencies = [ "ripemd", "sapling-crypto", "secp256k1", + "serde", "sha2", "subtle", "tracing", @@ -5433,7 +5575,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.20.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "bellman", "blake2b_simd", @@ -5455,10 +5597,11 @@ dependencies = [ [[package]] name = "zcash_protocol" version = "0.4.1" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "document-features", "memuse", + "serde", ] [[package]] @@ -5526,7 +5669,7 @@ dependencies = [ [[package]] name = "zip321" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash?rev=f1724c436a99208e612d8dec2119613fd280492d#f1724c436a99208e612d8dec2119613fd280492d" +source = "git+https://github.com/zcash/librustzcash.git?rev=e3d2c2163f2ede82b3837659b0616f36b1e404db#e3d2c2163f2ede82b3837659b0616f36b1e404db" dependencies = [ "base64", "nom", diff --git a/backend-lib/Cargo.toml b/backend-lib/Cargo.toml index 17c201076..d7f7b1531 100644 --- a/backend-lib/Cargo.toml +++ b/backend-lib/Cargo.toml @@ -15,7 +15,7 @@ rust-version = "1.82" orchard = "0.10" sapling = { package = "sapling-crypto", version = "0.3", default-features = false } zcash_address = "0.6" -zcash_client_backend = { version = "0.15", features = ["orchard", "tor", "transparent-inputs", "unstable"] } +zcash_client_backend = { version = "0.15", features = ["orchard", "tor", "transparent-inputs", "unstable", "pczt"] } zcash_client_sqlite = { version = "0.13", features = ["orchard", "transparent-inputs", "unstable"] } zcash_primitives = "0.20" zcash_proofs = "0.20" @@ -72,8 +72,12 @@ path = "src/main/rust/lib.rs" crate-type = ["staticlib", "cdylib"] [patch.crates-io] -zcash_address = { git = "https://github.com/zcash/librustzcash", rev = "f1724c436a99208e612d8dec2119613fd280492d" } -zcash_client_backend = { git = "https://github.com/zcash/librustzcash", rev = "f1724c436a99208e612d8dec2119613fd280492d" } -zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash", rev = "f1724c436a99208e612d8dec2119613fd280492d" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash", rev = "f1724c436a99208e612d8dec2119613fd280492d" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash", rev = "f1724c436a99208e612d8dec2119613fd280492d" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "e3d2c2163f2ede82b3837659b0616f36b1e404db" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "bcd08e1d23e70c42a338f3e3f79d6f4c0c219805" } +sapling-crypto = { git = "https://github.com/zcash/sapling-crypto.git", rev = "42a1de5a20007050fd3169b5da1cc28962dcb258" } diff --git a/backend-lib/src/main/rust/lib.rs b/backend-lib/src/main/rust/lib.rs index 34264899b..02f6c7aea 100644 --- a/backend-lib/src/main/rust/lib.rs +++ b/backend-lib/src/main/rust/lib.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::convert::{Infallible, TryFrom, TryInto}; use std::error::Error; use std::num::{NonZeroU32, NonZeroUsize}; @@ -32,8 +33,9 @@ use zcash_client_backend::{ chain::{scan_cached_blocks, CommitmentTreeRoot, ScanSummary}, scanning::{ScanPriority, ScanRange}, wallet::{ - create_proposed_transactions, decrypt_and_store_transaction, - input_selection::GreedyInputSelector, propose_shielding, propose_transfer, + create_proposed_transaction_pczt, create_proposed_transactions, + decrypt_and_store_transaction, input_selection::GreedyInputSelector, propose_shielding, + propose_transfer, }, Account, AccountBalance, AccountBirthday, InputSource, SeedRelevance, WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite, @@ -284,7 +286,9 @@ fn encode_account<'a, P: Parameters>( }; let seed_fingerprint = match account.source().key_derivation() { - Some(d) => env.byte_array_from_slice(&d.seed_fingerprint().to_bytes()[..])?.into(), + Some(d) => env + .byte_array_from_slice(&d.seed_fingerprint().to_bytes()[..])? + .into(), None => JObject::null(), }; @@ -303,7 +307,7 @@ fn encode_account<'a, P: Parameters>( hd_account_index, (&key_source).into(), (&seed_fingerprint).into(), - (&ufvk).into() + (&ufvk).into(), ], ) } @@ -1958,6 +1962,95 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_createPro unwrap_exc_or(&mut env, res, ptr::null_mut()) } +/// Creates a partially-constructed (unsigned without proofs) transaction from the given proposal. +/// +/// Returns the partially constructed transaction in the `postcard` format generated by the `pczt` +/// crate. +/// +/// Do not call this multiple times in parallel, or you will generate pczt instances that, if +/// finalized, would double-spend the same notes. +/// +/// # Safety +/// +/// - `db_data` must be non-null and valid for reads for `db_data_len` bytes, and it must +/// have an alignment of `1`. Its contents must be a string representing a valid system +/// path in the operating system's preferred representation. +/// - The memory referenced by `db_data` must not be mutated for the duration of the +/// function call. +/// - The total size `db_data_len` must be no larger than `isize::MAX`. See the safety +/// documentation of `pointer::offset`. +/// - `proposal_ptr` must be non-null and valid for reads for `proposal_len` bytes, and it +/// must have an alignment of `1`. Its contents must be an encoded Proposal protobuf. +/// - The memory referenced by `proposal_ptr` must not be mutated for the duration of the +/// function call. +/// - The total size `proposal_len` must be no larger than `isize::MAX`. See the safety +/// documentation of `pointer::offset`. +/// - `ufvk` must be non-null and must point to a null-terminated UTF-8 string. +#[unsafe(no_mangle)] +pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_createProposedTransactionPczt< + 'local, +>( + mut env: JNIEnv<'local>, + _: JClass<'local>, + db_data: JString<'local>, + proposal: JByteArray<'local>, + ufvk_string: JString<'local>, + network_id: jint, +) -> jbyteArray { + let res = catch_unwind(&mut env, |env| { + let _span = tracing::info_span!("RustBackend.createProposedTransactionPczt").entered(); + let network = parse_network(network_id as u32)?; + let mut db_data = wallet_db(env, network, db_data)?; + let ufvk_string = utils::java_string_to_rust(env, &ufvk_string); + let ufvk = match UnifiedFullViewingKey::decode(&network, &ufvk_string) { + Ok(ufvk) => ufvk, + Err(e) => { + return Err(anyhow!( + "Error while deriving viewing key from string input: {}", + e, + )); + } + }; + + let proposal = Proposal::decode(&env.convert_byte_array(proposal)?[..]) + .map_err(|e| anyhow!("Invalid proposal: {}", e))? + .try_into_standard_proposal(&db_data)?; + + let mut unused_transparent_outputs = HashMap::new(); + + let account_id = db_data + .get_account_for_ufvk(&ufvk) + .map_err(|e| anyhow!("Error retrieving account information for ufvk: {}", e))? + .ok_or(anyhow!( + "UFVK does not correspond to an account in the wallet." + ))? + .id(); + + if proposal.steps().len() == 1 { + let pczt = create_proposed_transaction_pczt::<_, _, Infallible, _, Infallible, _>( + &mut db_data, + &network, + &ufvk, + account_id, + OvkPolicy::Sender, + proposal.fee_rule(), + proposal.min_target_height(), + &[], + &proposal.steps().head, + &mut unused_transparent_outputs, + ) + .map_err(|e| anyhow!("Error creating PCZT from single-step proposal: {}", e))?; + + Ok(utils::rust_bytes_to_java(&env, &pczt.serialize())?.into_raw()) + } else { + Err(anyhow!( + "Multi-step proposals are not yet supported for PCZT generation." + )) + } + }); + unwrap_exc_or(&mut env, res, ptr::null_mut()) +} + #[unsafe(no_mangle)] pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_branchIdForHeight<'local>( mut env: JNIEnv<'local>,