From fd675c3205fbd121e659297f216ebc94d95cb074 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 1 Mar 2024 19:45:44 +0000 Subject: [PATCH] rust: Migrate to `zcash_primitives 0.14.0` --- .github/workflows/audits.yml | 2 +- Cargo.lock | 228 +++++------ Cargo.toml | 12 +- qa/rpc-tests/finalorchardroot.py | 2 +- qa/rpc-tests/finalsaplingroot.py | 25 +- qa/supply-chain/audits.toml | 30 ++ qa/supply-chain/config.toml | 58 +-- qa/supply-chain/imports.lock | 85 ++-- src/gtest/test_checktransaction.cpp | 32 +- src/gtest/test_coins.cpp | 9 +- src/gtest/test_dynamicusage.cpp | 18 +- src/gtest/test_keys.cpp | 2 +- src/gtest/test_mempoollimit.cpp | 6 +- src/gtest/test_transaction_builder.cpp | 41 +- src/gtest/test_validation.cpp | 2 +- src/gtest/test_zip32.cpp | 121 +++--- src/gtest/utils.cpp | 8 +- src/miner.cpp | 13 +- src/rust/bin/inspect/context.rs | 28 +- src/rust/bin/inspect/keys.rs | 16 +- src/rust/bin/inspect/main.rs | 3 +- src/rust/bin/inspect/transaction.rs | 27 +- src/rust/include/rust/builder.h | 3 +- src/rust/src/address_ffi.rs | 1 - src/rust/src/bridge.rs | 9 +- src/rust/src/builder_ffi.rs | 38 +- src/rust/src/incremental_merkle_tree.rs | 17 +- src/rust/src/init.rs | 17 +- src/rust/src/lib.rs | 13 +- src/rust/src/merkle_frontier.rs | 6 +- src/rust/src/note_encryption.rs | 34 +- src/rust/src/orchard_keys_ffi.rs | 6 +- src/rust/src/sapling.rs | 362 ++++++++---------- src/rust/src/sapling/spec.rs | 14 +- src/rust/src/sapling/zip32.rs | 53 +-- src/rust/src/test_harness_ffi.rs | 39 +- src/rust/src/tests/key_components.rs | 13 +- src/rust/src/tests/mod.rs | 2 +- src/rust/src/tests/signatures.rs | 239 ++++++------ src/rust/src/transaction_ffi.rs | 7 +- src/rust/src/unified_keys_ffi.rs | 4 +- src/rust/src/wallet.rs | 9 +- src/rust/src/wallet_scanner.rs | 15 +- src/test/coins_tests.cpp | 5 +- src/test/key_tests.cpp | 2 +- src/transaction_builder.cpp | 14 +- src/transaction_builder.h | 7 +- src/util/test.cpp | 3 +- .../asyncrpcoperation_saplingmigration.cpp | 12 +- .../asyncrpcoperation_saplingmigration.h | 3 +- src/wallet/gtest/test_orchard_wallet.cpp | 4 +- src/wallet/gtest/test_rpc_wallet.cpp | 126 +++--- src/wallet/gtest/test_wallet.cpp | 49 ++- src/wallet/gtest/test_wallet_zkeys.cpp | 4 +- src/wallet/test/rpc_wallet_tests.cpp | 4 +- src/wallet/wallet.cpp | 6 +- src/wallet/wallet_tx_builder.cpp | 6 +- src/zcash/address/zip32.cpp | 26 +- src/zcash/address/zip32.h | 2 - src/zcbenchmarks.cpp | 11 +- 60 files changed, 960 insertions(+), 993 deletions(-) diff --git a/.github/workflows/audits.yml b/.github/workflows/audits.yml index 29b54f5c7f..e53d3ff692 100644 --- a/.github/workflows/audits.yml +++ b/.github/workflows/audits.yml @@ -17,7 +17,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable id: toolchain - run: rustup override set ${{steps.toolchain.outputs.name}} - - run: cargo install cargo-vet --version ~0.8 + - run: cargo install cargo-vet --version ~0.9 - run: cargo vet --locked cargo-deny: diff --git a/Cargo.lock b/Cargo.lock index f3bdc2f583..11e61be297 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,12 +265,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - [[package]] name = "byteorder" version = "1.5.0" @@ -540,6 +534,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -583,12 +586,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "errno" version = "0.3.8" @@ -633,13 +630,10 @@ checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" [[package]] name = "fixed-hash" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ - "byteorder", - "rand", - "rustc-hex", "static_assertions", ] @@ -926,26 +920,6 @@ dependencies = [ "want", ] -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "incrementalmerkletree" version = "0.5.0" @@ -966,16 +940,6 @@ dependencies = [ "hashbrown 0.12.3", ] -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", -] - [[package]] name = "inout" version = "0.1.3" @@ -1098,6 +1062,8 @@ dependencies = [ "rand", "rand_core", "rayon", + "redjubjub", + "sapling-crypto", "secp256k1", "secrecy", "serde", @@ -1137,6 +1103,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "log" version = "0.4.20" @@ -1205,7 +1177,7 @@ checksum = "1d4fa7ce7c4862db464a37b0b31d89bca874562f034bd7993895572783d02950" dependencies = [ "base64", "hyper", - "indexmap 1.9.3", + "indexmap", "ipnet", "metrics", "metrics-util", @@ -1369,9 +1341,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d31e68534df32024dcc89a8390ec6d7bef65edd87d91b45cfb481a2eb2d77c5" +checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" dependencies = [ "aes", "bitvec", @@ -1394,6 +1366,8 @@ dependencies = [ "subtle", "tracing", "zcash_note_encryption", + "zcash_spec", + "zip32", ] [[package]] @@ -1411,32 +1385,6 @@ dependencies = [ "group", ] -[[package]] -name = "parity-scale-codec" -version = "3.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "password-hash" version = "0.3.2" @@ -1570,25 +1518,14 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitive-types" -version = "0.11.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec", "uint", ] -[[package]] -name = "proc-macro-crate" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" -dependencies = [ - "toml_datetime", - "toml_edit", -] - [[package]] name = "proc-macro2" version = "1.0.74" @@ -1848,12 +1785,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - [[package]] name = "rustc_version" version = "0.4.0" @@ -1894,6 +1825,39 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "sapling-crypto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d183012062dfdde85f7e3e758328fcf6e9846d8dd3fce35b04d0efcb6677b0e0" +dependencies = [ + "aes", + "bellman", + "bitvec", + "blake2b_simd", + "blake2s_simd", + "bls12_381", + "byteorder", + "document-features", + "ff", + "fpe", + "group", + "hex", + "incrementalmerkletree", + "jubjub", + "lazy_static", + "memuse", + "proptest", + "rand", + "rand_core", + "redjubjub", + "subtle", + "tracing", + "zcash_note_encryption", + "zcash_spec", + "zip32", +] + [[package]] name = "secp256k1" version = "0.26.0" @@ -2179,23 +2143,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - [[package]] name = "tower-service" version = "0.3.2" @@ -2640,15 +2587,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "winnow" -version = "0.5.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" -dependencies = [ - "memchr", -] - [[package]] name = "wyz" version = "0.5.1" @@ -2666,9 +2604,9 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "zcash_address" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8944af5c206cf2e37020ad54618e1825501b98548d35a638b73e0ec5762df8d5" +checksum = "bce173f1d9ed4f806e310bc3a873301531e7a6dc209928584d6404e3f8228ef4" dependencies = [ "bech32", "bs58", @@ -2688,9 +2626,9 @@ dependencies = [ [[package]] name = "zcash_history" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb611a28a4e13ac715ee712f4344d6b279b767daf6345dafefb2c4bf582b6679" +checksum = "2fde17bf53792f9c756b313730da14880257d7661b5bfc69d0571c3a7c11a76d" dependencies = [ "blake2b_simd", "byteorder", @@ -2712,17 +2650,15 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d17e4c94ca8d69d2fcf2be97522da5732a580eb2125cda3b150761952f8df8e6" +checksum = "9070e084570bb78aed4f8d71fd6254492e62c87a5d01e084183980e98117092d" dependencies = [ "aes", "bip0039", - "bitvec", "blake2b_simd", - "blake2s_simd", - "bls12_381", "byteorder", + "document-features", "equihash", "ff", "fpe", @@ -2731,31 +2667,36 @@ dependencies = [ "hex", "incrementalmerkletree", "jubjub", - "lazy_static", "memuse", "nonempty", "orchard", "proptest", "rand", "rand_core", + "redjubjub", "ripemd", + "sapling-crypto", "secp256k1", "sha2", "subtle", + "tracing", "zcash_address", "zcash_encoding", "zcash_note_encryption", + "zcash_spec", + "zip32", ] [[package]] name = "zcash_proofs" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0c99f65a840ff256c106b28d67d702d9759d206112473d4982c92003262406" +checksum = "b8a02eb1f151d9b9a6e16408d2c55ff440bd2fb232b7377277146d0fa2df9bc8" dependencies = [ "bellman", "blake2b_simd", "bls12_381", + "document-features", "group", "home", "jubjub", @@ -2763,11 +2704,21 @@ dependencies = [ "lazy_static", "rand_core", "redjubjub", + "sapling-crypto", "tracing", "xdg", "zcash_primitives", ] +[[package]] +name = "zcash_spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" +dependencies = [ + "blake2b_simd", +] + [[package]] name = "zerocopy" version = "0.7.32" @@ -2807,3 +2758,14 @@ dependencies = [ "quote", "syn 2.0.46", ] + +[[package]] +name = "zip32" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22" +dependencies = [ + "blake2b_simd", + "memuse", + "subtle", +] diff --git a/Cargo.toml b/Cargo.toml index aa4da87911..69d11c381e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,19 +52,21 @@ libc = "0.2" jubjub = "0.10" memuse = "0.2" nonempty = "0.7" -orchard = "0.6" +orchard = "0.7" +sapling = { package = "sapling-crypto", version = "0.1", features = ["temporary-zcashd"] } secp256k1 = "0.26" subtle = "2.2" rand_core = "0.6" +redjubjub = "0.7" tracing = "0.1" tracing-core = "0.1" tracing-appender = "0.2" zcash_address = "0.3" zcash_encoding = "0.2" -zcash_history = "0.3" +zcash_history = "0.4" zcash_note_encryption = "0.4" -zcash_primitives = { version = "0.13", features = ["temporary-zcashd", "transparent-inputs"] } -zcash_proofs = { version = "0.13", features = ["directories"] } +zcash_primitives = { version = "0.14", features = ["temporary-zcashd", "transparent-inputs"] } +zcash_proofs = { version = "0.14", features = ["directories"] } ed25519-zebra = "4" zeroize = "1.4.2" wagyu-zcash-parameters = "0.2" @@ -109,7 +111,7 @@ time = { version = "0.3", features = ["formatting", "macros"] } [dev-dependencies] incrementalmerkletree = { version = "0.5", features = ["test-dependencies"] } proptest = "1.0.0" -zcash_primitives = { version = "0.13", features = ["temporary-zcashd", "transparent-inputs", "test-dependencies"] } +zcash_primitives = { version = "0.14", features = ["temporary-zcashd", "transparent-inputs", "test-dependencies"] } [dependencies.tracing-subscriber] version = "0.3" diff --git a/qa/rpc-tests/finalorchardroot.py b/qa/rpc-tests/finalorchardroot.py index 33d6605a4a..d5a478b8df 100755 --- a/qa/rpc-tests/finalorchardroot.py +++ b/qa/rpc-tests/finalorchardroot.py @@ -237,7 +237,7 @@ def run_test(self): assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"] assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"] assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64) - assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 70) + assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 134) treestate = new_treestate # Mine a block with an Orchard shielded recipient and verify the final Orchard root changes diff --git a/qa/rpc-tests/finalsaplingroot.py b/qa/rpc-tests/finalsaplingroot.py index 7bb38241d2..ed4914ac74 100755 --- a/qa/rpc-tests/finalsaplingroot.py +++ b/qa/rpc-tests/finalsaplingroot.py @@ -122,8 +122,8 @@ def run_test(self): # Verify there is a Sapling output description (its commitment was added to tree) result = self.nodes[0].getrawtransaction(mytxid, 1) - assert_equal(len(result["vShieldedOutput"]), 1) - assert_equal(blk["trees"]["sapling"]["size"], 1) + assert_equal(len(result["vShieldedOutput"]), 2) # Non-coinbase bundles are padded + assert_equal(blk["trees"]["sapling"]["size"], 2) # Since there is a now sapling shielded input in the blockchain, # the sapling values should have changed @@ -133,7 +133,7 @@ def run_test(self): assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"] assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"] assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64) - assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 70) + assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 134) treestate = new_treestate # Mine an empty block and verify the final Sapling root does not change @@ -142,7 +142,7 @@ def run_test(self): self.sync_all() blk = self.nodes[0].getblock("202") assert_equal(root, blk["finalsaplingroot"]) - assert_equal(blk["trees"]["sapling"]["size"], 1) + assert_equal(blk["trees"]["sapling"]["size"], 2) # Mine a block with a transparent tx and verify the final Sapling root does not change taddr1 = self.nodes[1].getnewaddress() @@ -171,7 +171,7 @@ def run_test(self): assert_equal(len(blk["tx"]), 2) assert_equal(self.nodes[0].z_getbalance(zaddr0), Decimal("37.66")) assert_equal(root, blk["finalsaplingroot"]) - assert_equal(blk["trees"]["sapling"]["size"], 1) + assert_equal(blk["trees"]["sapling"]["size"], 2) new_treestate = self.nodes[0].z_gettreestate(str(-1)) assert_equal(new_treestate["sapling"]["commitments"]["finalRoot"], root) @@ -200,14 +200,14 @@ def run_test(self): # Verify there is a Sapling output description (its commitment was added to tree) result = self.nodes[0].getrawtransaction(mytxid, 1) assert_equal(len(result["vShieldedOutput"]), 2) # there is Sapling shielded change - assert_equal(blk["trees"]["sapling"]["size"], 3) + assert_equal(blk["trees"]["sapling"]["size"], 4) new_treestate = self.nodes[0].z_gettreestate(str(-1)) assert_equal(new_treestate["sprout"], treestate["sprout"]) assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"] assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"] assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64) - assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 136) + assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 200) treestate = new_treestate # Mine a block with a Sapling shielded sender and transparent recipient. @@ -221,16 +221,19 @@ def run_test(self): self.nodes[0].generate(1) self.sync_all() - assert_equal(len(self.nodes[0].getblock("206")["tx"]), 2) + blk = self.nodes[0].getblock("206") + assert_equal(len(blk["tx"]), 2) + assert_equal(mytxid, blk["tx"][1]) assert_equal(self.nodes[0].z_getbalance(taddr2), Decimal("2.34")) assert_equal(self.nodes[1].z_getbalance(saplingAddr1), 0) # Verify the final Sapling root changes (because the Sapling bundle was padded # with 2 dummy outputs). - blk = self.nodes[0].getblock("206") + print(self.nodes[0].getrawtransaction(blk["tx"][0])) + print(self.nodes[0].getrawtransaction(blk["tx"][1])) root = blk["finalsaplingroot"] assert root != self.nodes[0].getblock("205")["finalsaplingroot"] - assert_equal(blk["trees"]["sapling"]["size"], 5) + assert_equal(blk["trees"]["sapling"]["size"], 6) # Verify there are two Sapling output descriptions. result = self.nodes[0].getrawtransaction(mytxid, 1) @@ -241,7 +244,7 @@ def run_test(self): assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"] assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"] assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64) - assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 138) + assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 202) treestate = new_treestate # Activate NU5; more testing should be added once we can mine orchard transactions. diff --git a/qa/supply-chain/audits.toml b/qa/supply-chain/audits.toml index c9de9c803b..1816997ec7 100644 --- a/qa/supply-chain/audits.toml +++ b/qa/supply-chain/audits.toml @@ -2634,6 +2634,12 @@ user-id = 1244 # ebfull start = "2022-10-19" end = "2024-09-21" +[[trusted.sapling-crypto]] +criteria = ["safe-to-deploy", "crypto-reviewed", "license-reviewed"] +user-id = 6289 # str4d +start = "2024-01-26" +end = "2025-03-18" + [[trusted.windows-sys]] criteria = "safe-to-deploy" user-id = 64539 # Kenny Kerr (kennykerr) @@ -2694,6 +2700,12 @@ user-id = 1244 # ebfull start = "2022-10-19" end = "2024-09-21" +[[trusted.zcash_address]] +criteria = "safe-to-deploy" +user-id = 6289 # str4d +start = "2021-03-07" +end = "2025-03-18" + [[trusted.zcash_encoding]] criteria = "safe-to-deploy" user-id = 1244 # ebfull @@ -2706,6 +2718,12 @@ user-id = 1244 # ebfull start = "2020-03-04" end = "2024-09-21" +[[trusted.zcash_history]] +criteria = "safe-to-deploy" +user-id = 6289 # str4d +start = "2024-03-01" +end = "2025-03-18" + [[trusted.zcash_note_encryption]] criteria = ["safe-to-deploy", "crypto-reviewed"] user-id = 169181 # Kris Nuttycombe (nuttycom) @@ -2729,3 +2747,15 @@ criteria = ["safe-to-deploy", "crypto-reviewed", "license-reviewed"] user-id = 6289 # str4d start = "2021-03-26" end = "2024-09-21" + +[[trusted.zcash_spec]] +criteria = ["safe-to-deploy", "crypto-reviewed", "license-reviewed"] +user-id = 6289 # str4d +start = "2023-12-07" +end = "2025-03-18" + +[[trusted.zip32]] +criteria = "safe-to-deploy" +user-id = 6289 # str4d +start = "2023-12-06" +end = "2025-03-18" diff --git a/qa/supply-chain/config.toml b/qa/supply-chain/config.toml index bd8dc22f74..859eaea43b 100644 --- a/qa/supply-chain/config.toml +++ b/qa/supply-chain/config.toml @@ -2,7 +2,7 @@ # cargo-vet config file [cargo-vet] -version = "0.8" +version = "0.9" [imports.bytecode-alliance] url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml" @@ -91,10 +91,6 @@ criteria = "safe-to-deploy" version = "0.5.0" criteria = "safe-to-deploy" -[[exemptions.byte-slice-cast]] -version = "1.2.1" -criteria = "safe-to-deploy" - [[exemptions.byteorder]] version = "1.4.3" criteria = "safe-to-deploy" @@ -195,16 +191,12 @@ criteria = "safe-to-deploy" version = "3.0.0" criteria = "safe-to-deploy" -[[exemptions.equivalent]] -version = "1.0.0" -criteria = "safe-to-deploy" - [[exemptions.ff]] version = "0.12.0" criteria = "safe-to-deploy" [[exemptions.fixed-hash]] -version = "0.7.0" +version = "0.8.0" criteria = "safe-to-deploy" [[exemptions.fpe]] @@ -279,22 +271,10 @@ criteria = "safe-to-deploy" version = "0.14.25" criteria = "safe-to-deploy" -[[exemptions.impl-codec]] -version = "0.6.0" -criteria = "safe-to-deploy" - -[[exemptions.impl-trait-for-tuples]] -version = "0.2.2" -criteria = "safe-to-deploy" - [[exemptions.indexmap]] version = "1.8.1" criteria = "safe-to-deploy" -[[exemptions.indexmap]] -version = "2.0.0" -criteria = "safe-to-deploy" - [[exemptions.ipnet]] version = "2.5.0" criteria = "safe-to-deploy" @@ -363,10 +343,6 @@ criteria = "safe-to-deploy" version = "0.26.1" criteria = "safe-to-deploy" -[[exemptions.nom]] -version = "7.1.1" -criteria = "safe-to-deploy" - [[exemptions.nonempty]] version = "0.7.0" criteria = "safe-to-deploy" @@ -387,14 +363,6 @@ criteria = "safe-to-deploy" version = "0.22.0" criteria = "safe-to-deploy" -[[exemptions.parity-scale-codec]] -version = "3.6.1" -criteria = "safe-to-deploy" - -[[exemptions.parity-scale-codec-derive]] -version = "3.6.5" -criteria = "safe-to-deploy" - [[exemptions.password-hash]] version = "0.3.2" criteria = "safe-to-deploy" @@ -444,11 +412,7 @@ version = "0.2.16" criteria = "safe-to-deploy" [[exemptions.primitive-types]] -version = "0.11.1" -criteria = "safe-to-deploy" - -[[exemptions.proc-macro-crate]] -version = "1.2.1" +version = "0.12.2" criteria = "safe-to-deploy" [[exemptions.proptest]] @@ -515,10 +479,6 @@ criteria = "safe-to-deploy" version = "0.1.3" criteria = "safe-to-deploy" -[[exemptions.rustc-hex]] -version = "2.1.0" -criteria = "safe-to-deploy" - [[exemptions.rustix]] version = "0.38.28" criteria = "safe-to-deploy" @@ -579,10 +539,6 @@ criteria = "safe-to-deploy" version = "0.7.3" criteria = "safe-to-deploy" -[[exemptions.static_assertions]] -version = "1.1.0" -criteria = "safe-to-deploy" - [[exemptions.subtle]] version = "2.4.1" criteria = "safe-to-deploy" @@ -611,10 +567,6 @@ criteria = "safe-to-deploy" version = "1.35.0" criteria = "safe-to-deploy" -[[exemptions.toml_edit]] -version = "0.19.15" -criteria = "safe-to-deploy" - [[exemptions.tower-service]] version = "0.3.2" criteria = "safe-to-deploy" @@ -691,10 +643,6 @@ criteria = "safe-to-deploy" version = "0.4.0" criteria = "safe-to-deploy" -[[exemptions.winnow]] -version = "0.5.31" -criteria = "safe-to-deploy" - [[exemptions.wyz]] version = "0.5.0" criteria = "safe-to-deploy" diff --git a/qa/supply-chain/imports.lock b/qa/supply-chain/imports.lock index b1cc37600f..2a14cfbe3b 100644 --- a/qa/supply-chain/imports.lock +++ b/qa/supply-chain/imports.lock @@ -52,8 +52,14 @@ user-id = 6289 user-login = "str4d" [[publisher.orchard]] -version = "0.6.0" -when = "2023-09-08" +version = "0.7.1" +when = "2024-02-29" +user-id = 6289 +user-login = "str4d" + +[[publisher.sapling-crypto]] +version = "0.1.1" +when = "2024-02-15" user-id = 6289 user-login = "str4d" @@ -184,10 +190,10 @@ user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.zcash_address]] -version = "0.3.0" -when = "2023-06-06" -user-id = 1244 -user-login = "ebfull" +version = "0.3.1" +when = "2024-01-12" +user-id = 6289 +user-login = "str4d" [[publisher.zcash_encoding]] version = "0.2.0" @@ -196,10 +202,10 @@ user-id = 1244 user-login = "ebfull" [[publisher.zcash_history]] -version = "0.3.0" -when = "2022-05-11" -user-id = 1244 -user-login = "ebfull" +version = "0.4.0" +when = "2024-03-01" +user-id = 6289 +user-login = "str4d" [[publisher.zcash_note_encryption]] version = "0.4.0" @@ -209,14 +215,26 @@ user-login = "nuttycom" user-name = "Kris Nuttycombe" [[publisher.zcash_primitives]] -version = "0.13.0" -when = "2023-09-25" +version = "0.14.0" +when = "2024-03-01" user-id = 6289 user-login = "str4d" [[publisher.zcash_proofs]] -version = "0.13.0" -when = "2023-09-25" +version = "0.14.0" +when = "2024-03-01" +user-id = 6289 +user-login = "str4d" + +[[publisher.zcash_spec]] +version = "0.1.0" +when = "2023-12-07" +user-id = 6289 +user-login = "str4d" + +[[publisher.zip32]] +version = "0.1.0" +when = "2023-12-06" user-id = 6289 user-login = "str4d" @@ -466,6 +484,12 @@ criteria = "safe-to-deploy" version = "0.1.4" notes = "I always really enjoy reading eliza's code, she left perfect comments at every use of unsafe." +[[audits.bytecode-alliance.audits.static_assertions]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "1.1.0" +notes = "No dependencies and completely a compile-time crate as advertised. Uses `unsafe` in one module as a compile-time check only: `mem::transmute` and `ptr::write` are wrapped in an impossible-to-run closure." + [[audits.bytecode-alliance.audits.tempfile]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -553,12 +577,6 @@ criteria = "safe-to-deploy" version = "1.0.40" notes = "Found no unsafe or ambient capabilities used" -[[audits.embark-studios.audits.toml_datetime]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -delta = "0.6.1 -> 0.6.2" -notes = "No notable changes" - [[audits.embark-studios.audits.valuable]] who = "Johan Andersson " criteria = "safe-to-deploy" @@ -597,6 +615,15 @@ are made about the safety of either of those libraries. :) """ aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.nom]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +version = "7.1.3" +notes = """ +Reviewed in https://chromium-review.googlesource.com/c/chromium/src/+/5046153 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.pin-project-lite]] who = "David Koloski " criteria = "safe-to-deploy" @@ -1034,6 +1061,12 @@ criteria = "safe-to-deploy" delta = "0.10.3 -> 0.10.6" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.document-features]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +version = "0.2.8" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.either]] who = "Mike Hommey " criteria = "safe-to-deploy" @@ -1109,6 +1142,12 @@ version = "1.4.0" notes = "I have read over the macros, and audited the unsafe code." aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" +[[audits.mozilla.audits.litrs]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +version = "0.4.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.log]] who = "Mike Hommey " criteria = "safe-to-deploy" @@ -1128,12 +1167,6 @@ criteria = "safe-to-deploy" version = "0.4.1" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.nom]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "7.1.1 -> 7.1.3" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - [[audits.mozilla.audits.num-bigint]] who = "Josh Stone " criteria = "safe-to-deploy" diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index d3ccca24b4..479e230ae9 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -325,6 +325,8 @@ TEST(ChecktransactionTests, BadTxnsTxouttotalToolargeOutputs) { CheckTransactionWithoutProofVerification(tx, state); } +// TODO: The new Sapling bundle API prevents us from constructing this case. +/* TEST(ChecktransactionTests, ValueBalanceNonZero) { CMutableTransaction mtx = GetValidTransaction(NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId); mtx.saplingBundle = sapling::test_only_invalid_bundle(0, 0, 10); @@ -333,8 +335,9 @@ TEST(ChecktransactionTests, ValueBalanceNonZero) { MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false, "")).Times(1); - CheckTransactionWithoutProofVerification(tx, state); + EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state)); } +*/ TEST(ChecktransactionTests, ValueBalanceOverflowsTotal) { CMutableTransaction mtx = GetValidTransaction(NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId); @@ -1152,7 +1155,8 @@ TEST(ChecktransactionTests, HeartwoodAcceptsSaplingShieldedCoinbase) { RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); auto chainparams = Params(); - auto builder = sapling::new_builder(*chainparams.RustNetwork(), 10); + auto saplingAnchor = SaplingMerkleTree::empty_root().GetRawBytes(); + auto builder = sapling::new_builder(*chainparams.RustNetwork(), 10, saplingAnchor, true); builder->add_recipient( uint256().GetRawBytes(), libzcash::SaplingSpendingKey::random().default_address().GetRawBytes(), @@ -1167,7 +1171,7 @@ TEST(ChecktransactionTests, HeartwoodAcceptsSaplingShieldedCoinbase) { mtx.vin.resize(1); mtx.vin[0].prevout.SetNull(); mtx.vJoinSplit.resize(0); - mtx.saplingBundle = sapling::apply_bundle_signatures(sapling::build_bundle(std::move(builder), 10), {}); + mtx.saplingBundle = sapling::apply_bundle_signatures(sapling::build_bundle(std::move(builder)), {}); auto outputs = mtx.saplingBundle.GetDetails().outputs(); auto& odesc = outputs[0]; @@ -1256,10 +1260,12 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) { mtx.vin[0].prevout.SetNull(); mtx.vin[0].scriptSig << 123; mtx.vJoinSplit.resize(0); - mtx.saplingBundle = sapling::test_only_invalid_bundle(0, 0, -1000); + mtx.saplingBundle = sapling::test_only_invalid_bundle(0, 1, -1000); // Coinbase transaction should fail non-contextual checks with no shielded // outputs and non-zero valueBalanceSapling. + // TODO: The new Sapling bundle API prevents us from constructing this case. + /* { CTransaction tx(mtx); EXPECT_TRUE(tx.IsCoinBase()); @@ -1268,15 +1274,17 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) { EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false, "")).Times(1); EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state)); } + */ // Add a Sapling output. - auto builder = sapling::new_builder(*chainparams.RustNetwork(), 10); + auto saplingAnchor = SaplingMerkleTree::empty_root().GetRawBytes(); + auto builder = sapling::new_builder(*chainparams.RustNetwork(), 10, saplingAnchor, true); builder->add_recipient( uint256().GetRawBytes(), libzcash::SaplingSpendingKey::random().default_address().GetRawBytes(), 1000, libzcash::Memo::ToBytes(std::nullopt)); - mtx.saplingBundle = sapling::apply_bundle_signatures(sapling::build_bundle(std::move(builder), 10), {}); + mtx.saplingBundle = sapling::apply_bundle_signatures(sapling::build_bundle(std::move(builder)), {}); CTransaction tx(mtx); EXPECT_TRUE(tx.IsCoinBase()); @@ -1376,9 +1384,15 @@ TEST(ChecktransactionTests, InvalidOrchardShieldedCoinbase) { mtx.nConsensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_NU5].nBranchId; // Make it an invalid shielded coinbase, by creating an all-dummy Orchard bundle. + RawHDSeed seed(32, 0); + auto to = libzcash::OrchardSpendingKey::ForAccount(seed, 133, 0) + .ToFullViewingKey() + .GetChangeAddress(); mtx.vin.resize(1); mtx.vin[0].prevout.SetNull(); - mtx.orchardBundle = orchard::Builder(false, true, uint256()) + auto builder = orchard::Builder(true, uint256()); + builder.AddOutput(std::nullopt, to, 0, std::nullopt); + mtx.orchardBundle = builder .Build().value() .ProveAndSign({}, uint256()).value(); @@ -1405,7 +1419,7 @@ TEST(ChecktransactionTests, NU5AcceptsOrchardShieldedCoinbase) { auto chainparams = Params(); uint256 orchardAnchor; - auto builder = orchard::Builder(false, true, orchardAnchor); + auto builder = orchard::Builder(true, orchardAnchor); // Shielded coinbase outputs must be recoverable with an all-zeroes ovk. RawHDSeed rawSeed(32, 0); @@ -1527,7 +1541,7 @@ TEST(ChecktransactionTests, NU5EnforcesOrchardRulesOnShieldedCoinbase) { auto chainparams = Params(); uint256 orchardAnchor; - auto builder = orchard::Builder(false, true, orchardAnchor); + auto builder = orchard::Builder(true, orchardAnchor); // Shielded coinbase outputs must be recoverable with an all-zeroes ovk. RawHDSeed rawSeed(32, 0); diff --git a/src/gtest/test_coins.cpp b/src/gtest/test_coins.cpp index 6302817c39..53d5c674b4 100644 --- a/src/gtest/test_coins.cpp +++ b/src/gtest/test_coins.cpp @@ -448,11 +448,14 @@ class TxWithNullifiers mutableTxV5.saplingBundle = sapling::test_only_invalid_bundle(1, 0, 0); saplingNullifier = uint256::FromRawBytes(mutableTxV5.saplingBundle.GetDetails().spends()[0].nullifier()); - // The Orchard bundle builder always pads to two Actions, so we can just - // use an empty builder to create a dummy Orchard bundle. + RawHDSeed seed(32, 0); + auto to = libzcash::OrchardSpendingKey::ForAccount(seed, 133, 0) + .ToFullViewingKey() + .GetChangeAddress(); uint256 orchardAnchor; uint256 dataToBeSigned; - auto builder = orchard::Builder(true, true, orchardAnchor); + auto builder = orchard::Builder(false, orchardAnchor); + builder.AddOutput(std::nullopt, to, 0, std::nullopt); mutableTxV5.orchardBundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value(); orchardNullifier = mutableTxV5.orchardBundle.GetNullifiers().at(0); diff --git a/src/gtest/test_dynamicusage.cpp b/src/gtest/test_dynamicusage.cpp index 26e425524c..62df81888a 100644 --- a/src/gtest/test_dynamicusage.cpp +++ b/src/gtest/test_dynamicusage.cpp @@ -23,7 +23,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparent) auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID()); CTxDestination taddr = tsk.GetPubKey().GetID(); - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, SaplingMerkleTree::empty_root(), &keystore); builder.SetFee(10000); builder.AddTransparentInput( COutPoint(uint256S("7777777777777777777777777777777777777777777777777777777777777777"), 0), @@ -64,7 +64,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToSapling) auto pa = extfvk.DefaultAddress(); auto testNote = GetTestSaplingNote(pa, 50000); - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.SetFee(10000); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 5000, {}); @@ -73,7 +73,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToSapling) // 1 vShieldedSpend + 2 vShieldedOutput EXPECT_EQ(1, tx.GetSaplingSpendsCount()); EXPECT_EQ(2, tx.GetSaplingOutputsCount()); - EXPECT_EQ(400 + 2520, RecursiveDynamicUsage(tx)); + EXPECT_EQ(400 + 32 + 2520, RecursiveDynamicUsage(tx)); RegtestDeactivateSapling(); } @@ -89,7 +89,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling) auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID()); auto sk = libzcash::SaplingSpendingKey::random(); - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, SaplingMerkleTree::empty_root(), &keystore); builder.SetFee(10000); builder.AddTransparentInput( COutPoint(uint256S("7777777777777777777777777777777777777777777777777777777777777777"), 0), @@ -97,10 +97,10 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling) builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 40000, {}); auto tx = builder.Build().GetTxOrThrow(); - // 1 vin + 1 vShieldedOutput + // 1 vin + 2 vShieldedOutput EXPECT_EQ(0, tx.GetSaplingSpendsCount()); - EXPECT_EQ(1, tx.GetSaplingOutputsCount()); - EXPECT_EQ((96 + 128) + 1200, RecursiveDynamicUsage(tx)); + EXPECT_EQ(2, tx.GetSaplingOutputsCount()); + EXPECT_EQ((96 + 128) + 2280, RecursiveDynamicUsage(tx)); RegtestDeactivateSapling(); } @@ -117,7 +117,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToTransparent) auto sk = GetTestMasterSaplingSpendingKey(); auto testNote = GetTestSaplingNote(sk.ToXFVK().DefaultAddress(), 50000); - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root(), &keystore); builder.SetFee(10000); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddTransparentOutput(taddr, 40000); @@ -126,7 +126,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToTransparent) // 1 vShieldedSpend + 2 vShieldedOutput + 1 vout EXPECT_EQ(1, tx.GetSaplingSpendsCount()); EXPECT_EQ(2, tx.GetSaplingOutputsCount()); - EXPECT_EQ(400 + 2520 + 64, RecursiveDynamicUsage(tx)); + EXPECT_EQ(400 + 2520 + 64 + 32, RecursiveDynamicUsage(tx)); RegtestDeactivateSapling(); } diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index 0482e5fa51..7058d3caf1 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -20,7 +20,7 @@ TEST(Keys, EncodeAndDecodeSapling) auto m = GetTestMasterSaplingSpendingKey(); for (uint32_t i = 0; i < 1000; i++) { - auto sk = m.Derive(i); + auto sk = m.Derive(i | HARDENED_KEY_LIMIT); { std::string sk_string = keyIO.EncodeSpendingKey(sk); EXPECT_EQ( diff --git a/src/gtest/test_mempoollimit.cpp b/src/gtest/test_mempoollimit.cpp index 9abffbf2d4..b0952d0d32 100644 --- a/src/gtest/test_mempoollimit.cpp +++ b/src/gtest/test_mempoollimit.cpp @@ -124,7 +124,7 @@ TEST(MempoolLimitTests, MempoolCostAndEvictionWeight) // Default fee { - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); @@ -135,7 +135,7 @@ TEST(MempoolLimitTests, MempoolCostAndEvictionWeight) // Lower than standard fee { - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); static_assert(MINIMUM_FEE == 10000); @@ -148,7 +148,7 @@ TEST(MempoolLimitTests, MempoolCostAndEvictionWeight) // Larger Tx { - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); for (int i = 0; i < 10; i++) { builder.AddSaplingOutput(fvk.ovk, pa, 1000, {}); diff --git a/src/gtest/test_transaction_builder.cpp b/src/gtest/test_transaction_builder.cpp index 82fabc0bdd..2986d696f2 100644 --- a/src/gtest/test_transaction_builder.cpp +++ b/src/gtest/test_transaction_builder.cpp @@ -40,7 +40,7 @@ TEST(TransactionBuilder, TransparentToSapling) // Create a shielding transaction from transparent to Sapling // 0.00005 t-ZEC in, 0.00004 z-ZEC out, default fee - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, SaplingMerkleTree::empty_root(), &keystore); builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 5000); builder.AddSaplingOutput(fvk_from.ovk, pk, 4000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -49,7 +49,7 @@ TEST(TransactionBuilder, TransparentToSapling) EXPECT_EQ(tx.vout.size(), 0); EXPECT_EQ(tx.vJoinSplit.size(), 0); EXPECT_EQ(tx.GetSaplingSpendsCount(), 0); - EXPECT_EQ(tx.GetSaplingOutputsCount(), 1); + EXPECT_EQ(tx.GetSaplingOutputsCount(), 2); EXPECT_EQ(tx.GetValueBalanceSapling(), -4000); CValidationState state; @@ -74,7 +74,7 @@ TEST(TransactionBuilder, SaplingToSapling) { // Create a Sapling-only transaction // 0.00004 z-ZEC in, 0.000025 z-ZEC out, default fee, 0.000005 z-ZEC change - auto builder = TransactionBuilder(Params(), 2, std::nullopt); + auto builder = TransactionBuilder(Params(), 2, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); // Check that trying to add a different anchor fails @@ -119,7 +119,7 @@ TEST(TransactionBuilder, SaplingToSprout) { // - 0.00004 Sapling-ZEC in - 0.000025 Sprout-ZEC out // - 0.000005 Sapling-ZEC change // - default t-ZEC fee - auto builder = TransactionBuilder(Params(), 2, std::nullopt, nullptr); + auto builder = TransactionBuilder(Params(), 2, std::nullopt, testNote.tree.root(), nullptr); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSproutOutput(sproutAddr, 2500, std::nullopt); auto tx = builder.Build().GetTxOrThrow(); @@ -173,7 +173,7 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) { // - 0.00005 Sprout-ZEC change // - 0.00005 Sapling-ZEC out // - 0.00005 t-ZEC fee - auto builder = TransactionBuilder(Params(), 2, std::nullopt, nullptr, &view); + auto builder = TransactionBuilder(Params(), 2, std::nullopt, SaplingMerkleTree::empty_root(), nullptr, &view); builder.SetFee(5000); builder.AddSproutInput(sproutSk, sproutNote, sproutWitness); builder.AddSproutOutput(sproutAddr, 6000, std::nullopt); @@ -193,7 +193,7 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) { EXPECT_EQ(tx.vJoinSplit[2].vpub_old, 0); EXPECT_EQ(tx.vJoinSplit[2].vpub_new, 10000); EXPECT_EQ(tx.GetSaplingSpendsCount(), 0); - EXPECT_EQ(tx.GetSaplingOutputsCount(), 1); + EXPECT_EQ(tx.GetSaplingOutputsCount(), 2); EXPECT_EQ(tx.GetValueBalanceSapling(), -5000); CValidationState state; @@ -247,7 +247,7 @@ TEST(TransactionBuilder, TransparentToOrchard) // Create a shielding transaction from transparent to Orchard // 0.00005 t-ZEC in, 0.00004 z-ZEC out, default fee - auto builder = TransactionBuilder(Params(), 1, orchardAnchor, &keystore); + auto builder = TransactionBuilder(Params(), 1, orchardAnchor, SaplingMerkleTree::empty_root(), &keystore); builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 5000); builder.AddOrchardOutput(std::nullopt, recipient, 4000, std::nullopt); auto maybeTx = builder.Build(); @@ -279,7 +279,7 @@ TEST(TransactionBuilder, ThrowsOnTransparentInputWithoutKeyStore) SelectParams(CBaseChainParams::REGTEST); const Consensus::Params& consensusParams = Params().GetConsensus(); - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, SaplingMerkleTree::empty_root()); ASSERT_THROW(builder.AddTransparentInput(COutPoint(), CScript(), 1), std::runtime_error); } @@ -290,7 +290,7 @@ TEST(TransactionBuilder, RejectsInvalidTransparentOutput) // Default CTxDestination type is an invalid address CTxDestination taddr; - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, SaplingMerkleTree::empty_root()); ASSERT_THROW(builder.AddTransparentOutput(taddr, 50), UniValue); } @@ -317,13 +317,13 @@ TEST(TransactionBuilder, FailsWithNegativeChange) // Fail if there is only a Sapling output // 0.00005 z-ZEC out, default fee - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingOutput(fvk.ovk, pa, 5000, {}); EXPECT_EQ("Change cannot be negative: -0.00006 ZEC", builder.Build().GetError()); // Fail if there is only a transparent output // 0.00005 t-ZEC out, default fee - builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root(), &keystore); builder.AddTransparentOutput(taddr, 5000); EXPECT_EQ("Change cannot be negative: -0.00006 ZEC", builder.Build().GetError()); @@ -368,17 +368,18 @@ TEST(TransactionBuilder, ChangeOutput) auto scriptPubKey = GetScriptForDestination(tkeyid); auto fakeCoin = COutPoint(uint256S("7777777777777777777777777777777777777777777777777777777777777777"), 0); + auto saplingAnchor = SaplingMerkleTree::empty_root(); // No change address and no Sapling spends { - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, saplingAnchor, &keystore); builder.AddTransparentInput(fakeCoin, scriptPubKey, 2500); EXPECT_EQ("Could not determine change address", builder.Build().GetError()); } // Change to the same address as the first Sapling spend { - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root(), &keystore); builder.AddTransparentInput(fakeCoin, scriptPubKey, 2500); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); auto tx = builder.Build().GetTxOrThrow(); @@ -393,7 +394,7 @@ TEST(TransactionBuilder, ChangeOutput) // Change to a Sapling address { - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, saplingAnchor, &keystore); builder.AddTransparentInput(fakeCoin, scriptPubKey, 2500); builder.SendChangeTo(zChangeAddr, fvkOut.ovk); auto tx = builder.Build().GetTxOrThrow(); @@ -402,13 +403,13 @@ TEST(TransactionBuilder, ChangeOutput) EXPECT_EQ(tx.vout.size(), 0); EXPECT_EQ(tx.vJoinSplit.size(), 0); EXPECT_EQ(tx.GetSaplingSpendsCount(), 0); - EXPECT_EQ(tx.GetSaplingOutputsCount(), 1); + EXPECT_EQ(tx.GetSaplingOutputsCount(), 2); EXPECT_EQ(tx.GetValueBalanceSapling(), -1500); } // Change to a transparent address { - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, saplingAnchor, &keystore); builder.AddTransparentInput(fakeCoin, scriptPubKey, 2500); builder.SendChangeTo(tkeyid, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -442,7 +443,7 @@ TEST(TransactionBuilder, SetFee) // Default fee { - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -458,7 +459,7 @@ TEST(TransactionBuilder, SetFee) // Configured fee { - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); builder.SetFee(20000); @@ -487,7 +488,8 @@ TEST(TransactionBuilder, CheckSaplingTxVersion) auto pk = sk.ToXFVK().DefaultAddress(); // Cannot add Sapling outputs to a non-Sapling transaction - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + SaplingMerkleTree tree; + auto builder = TransactionBuilder(Params(), 1, std::nullopt, tree.root()); try { builder.AddSaplingOutput(uint256(), pk, 12345, {}); } catch (std::runtime_error const & err) { @@ -498,7 +500,6 @@ TEST(TransactionBuilder, CheckSaplingTxVersion) // Cannot add Sapling spends to a non-Sapling transaction libzcash::SaplingNote note(pk, 50000, libzcash::Zip212Enabled::BeforeZip212); - SaplingMerkleTree tree; try { builder.AddSaplingSpend(sk, note, tree.witness()); } catch (std::runtime_error const & err) { diff --git a/src/gtest/test_validation.cpp b/src/gtest/test_validation.cpp index 64a204f12a..b522f887e8 100644 --- a/src/gtest/test_validation.cpp +++ b/src/gtest/test_validation.cpp @@ -208,7 +208,7 @@ TEST(Validation, ContextualCheckInputsDetectsOldBranchId) { // Create a transparent transaction that spends the coin, targeting // a height during the Overwinter epoch. - auto builder = TransactionBuilder(params, 15, std::nullopt, &keystore); + auto builder = TransactionBuilder(params, 15, std::nullopt, SaplingMerkleTree::empty_root(), &keystore); builder.AddTransparentInput(utxo, scriptPubKey, coinValue); builder.AddTransparentOutput(destination, 4000); auto tx = builder.Build().GetTxOrThrow(); diff --git a/src/gtest/test_zip32.cpp b/src/gtest/test_zip32.cpp index 9361ccb728..eefdc2efe9 100644 --- a/src/gtest/test_zip32.cpp +++ b/src/gtest/test_zip32.cpp @@ -3,7 +3,7 @@ #include -// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py +// From https://github.com/zcash/zcash-test-vectors/blob/master/zcash_test_vectors/sapling/zip32.py // Sapling consistently uses little-endian encoding, but uint256S takes its input in // big-endian byte order, so the test vectors below are byte-reversed. TEST(ZIP32, TestVectors) { @@ -35,102 +35,95 @@ TEST(ZIP32, TestVectors) { m.ToXFVK().DefaultAddress().d, testing::ElementsAreArray({ 0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89 })); - auto m_1 = m.Derive(1); - EXPECT_EQ(m_1.depth, 1); - EXPECT_EQ(m_1.parentFVKTag, 0x3a71c214); - EXPECT_EQ(m_1.childIndex, 1); + auto m_1h = m.Derive(1 | HARDENED_KEY_LIMIT); + EXPECT_EQ(m_1h.depth, 1); + EXPECT_EQ(m_1h.parentFVKTag, 0x3a71c214); + EXPECT_EQ(m_1h.childIndex, 1 | HARDENED_KEY_LIMIT); EXPECT_EQ( - m_1.chaincode, - uint256S("e6bcda05678a43fad229334ef0b795a590e7c50590baf0d9b9031a690c114701")); + m_1h.chaincode, + uint256S("dbaeca68fd2ef8b45ec23ee91bd694aa2759e010c668bb3e066b20a845aacc6f")); EXPECT_EQ( - m_1.expsk.ask, - uint256S("0c357a2655b4b8d761794095df5cb402d3ba4a428cf6a88e7c2816a597c12b28")); + m_1h.expsk.ask, + uint256S("04bd31e1a6218db693ff0802f029043ec20f3b0b8b148cdc04be7afb2ee9f7d5")); EXPECT_EQ( - m_1.expsk.nsk, - uint256S("01ba6bff1018fd4eac04da7e3f2c6be9c229e662c5c4d1d6fc1ecafd8829a3e7")); + m_1h.expsk.nsk, + uint256S("0a75e557f6fcbf672e0134d4ec2d51a3f358659b4b5c46f303e6cb22687c2a37")); EXPECT_EQ( - m_1.expsk.ovk, - uint256S("7474a4c518551bd82f14a7f7365a8ffa403c50cfeffedf026ada8688fc81135f")); + m_1h.expsk.ovk, + uint256S("691c33ec470a1697ca37ceb237bb7f1691d2a833543514cf1f8c343319763025")); EXPECT_EQ( - m_1.dk, - uint256S("dcb4c170d878510e96c4a74192d7eecde9c9912b00b99a12ec91d7a232e84de0")); + m_1h.dk, + uint256S("26d53444cbe2e9929f619d810a0d05ae0deece0a72c3a7e3df9a5fd60f4088f2")); EXPECT_THAT( - m_1.ToXFVK().DefaultAddress().d, - testing::ElementsAreArray({ 0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81 })); + m_1h.ToXFVK().DefaultAddress().d, + testing::ElementsAreArray({ 0xbc, 0xc3, 0x23, 0xe8, 0xda, 0x39, 0xb4, 0x96, 0xc0, 0x50, 0x51 })); - auto m_1_2h = m_1.Derive(2 | HARDENED_KEY_LIMIT); - EXPECT_EQ(m_1_2h.depth, 2); - EXPECT_EQ(m_1_2h.parentFVKTag, 0x079e99db); - EXPECT_EQ(m_1_2h.childIndex, 2 | HARDENED_KEY_LIMIT); + auto m_1h_2h = m_1h.Derive(2 | HARDENED_KEY_LIMIT); + EXPECT_EQ(m_1h_2h.depth, 2); + EXPECT_EQ(m_1h_2h.parentFVKTag, 0xcb238476); + EXPECT_EQ(m_1h_2h.childIndex, 2 | HARDENED_KEY_LIMIT); EXPECT_EQ( - m_1_2h.chaincode, - uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97")); + m_1h_2h.chaincode, + uint256S("daf7be6f80503ab34f14f236da9de2cf540ae3c100f520607980d0756c087944")); EXPECT_EQ( - m_1_2h.expsk.ask, - uint256S("0dc6e4fe846bda925c82e632980434e17b51dac81fc4821fa71334ee3c11e88b")); + m_1h_2h.expsk.ask, + uint256S("06512f33a6f9ae4b42fd71f9cfa08d3727522dd3089cad596fc3139eb65df37f")); EXPECT_EQ( - m_1_2h.expsk.nsk, - uint256S("0c99a63a275c1c66734761cfb9c62fe9bd1b953f579123d3d0e769c59d057837")); + m_1h_2h.expsk.nsk, + uint256S("00debf5999f564a3e05a0d418cf40714399a32c1bdc98ba2eb4439a0e46e9c77")); EXPECT_EQ( - m_1_2h.expsk.ovk, - uint256S("bc1328fc5eb693e18875c5149d06953b11d39447ebd6e38c023c22962e1881cf")); + m_1h_2h.expsk.ovk, + uint256S("ac85619305763dc29b67b75e305e5323bda7d6a530736a88417f90bf0171fcd9")); EXPECT_EQ( - m_1_2h.dk, - uint256S("377bb062dce7e0dcd8a0054d0ca4b4d1481b3710bfa1df12ca46ff9e9fa1eda3")); + m_1h_2h.dk, + uint256S("d148325ff6faa682558de97a9fec61dd8dc10a96d0cd214bc531e0869a9e69e4")); EXPECT_THAT( - m_1_2h.ToXFVK().DefaultAddress().d, - testing::ElementsAreArray({ 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41 })); + m_1h_2h.ToXFVK().DefaultAddress().d, + testing::ElementsAreArray({ 0x98, 0x82, 0x40, 0xce, 0xa4, 0xdb, 0xc3, 0x0a, 0x73, 0x75, 0x50 })); - auto m_1_2hv = m_1_2h.ToXFVK(); + auto m_1_2hv = m_1h_2h.ToXFVK(); EXPECT_EQ(m_1_2hv.depth, 2); - EXPECT_EQ(m_1_2hv.parentFVKTag, 0x079e99db); + EXPECT_EQ(m_1_2hv.parentFVKTag, 0xcb238476); EXPECT_EQ(m_1_2hv.childIndex, 2 | HARDENED_KEY_LIMIT); EXPECT_EQ( m_1_2hv.chaincode, - uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97")); + uint256S("daf7be6f80503ab34f14f236da9de2cf540ae3c100f520607980d0756c087944")); EXPECT_EQ( m_1_2hv.fvk.ak, - uint256S("4138cffdf7200e52d4e9f4384481b4a4c4d070493a5e401e4ffa850f5a92c5a6")); + uint256S("4eab7275725c76ee4247ae8d941d4b53682e39da641785e097377144953f859a")); EXPECT_EQ( m_1_2hv.fvk.nk, - uint256S("11eee22577304f660cc036bc84b3fc88d1ec50ae8a4d657beb6b211659304e30")); + uint256S("be4f5d4f36018511d23a1b9a9c87af8c6dbd20212da84121c1ce884f8aa266f1")); EXPECT_EQ( m_1_2hv.fvk.ovk, - uint256S("bc1328fc5eb693e18875c5149d06953b11d39447ebd6e38c023c22962e1881cf")); + uint256S("ac85619305763dc29b67b75e305e5323bda7d6a530736a88417f90bf0171fcd9")); EXPECT_EQ( m_1_2hv.dk, - uint256S("377bb062dce7e0dcd8a0054d0ca4b4d1481b3710bfa1df12ca46ff9e9fa1eda3")); - EXPECT_EQ(m_1_2hv.DefaultAddress(), m_1_2h.ToXFVK().DefaultAddress()); + uint256S("d148325ff6faa682558de97a9fec61dd8dc10a96d0cd214bc531e0869a9e69e4")); + EXPECT_EQ(m_1_2hv.DefaultAddress(), m_1h_2h.ToXFVK().DefaultAddress()); - // Hardened derivation from an xfvk fails - EXPECT_FALSE(m_1_2hv.Derive(3 | HARDENED_KEY_LIMIT)); - - // Non-hardened derivation succeeds - auto maybe_m_1_2hv_3 = m_1_2hv.Derive(3); - EXPECT_TRUE(maybe_m_1_2hv_3); - - auto m_1_2hv_3 = maybe_m_1_2hv_3.value(); - EXPECT_EQ(m_1_2hv_3.depth, 3); - EXPECT_EQ(m_1_2hv_3.parentFVKTag, 0x7583c148); - EXPECT_EQ(m_1_2hv_3.childIndex, 3); + auto m_1h_2h_3h = m_1h_2h.Derive(3 | HARDENED_KEY_LIMIT); + EXPECT_EQ(m_1h_2h_3h.depth, 3); + EXPECT_EQ(m_1h_2h_3h.parentFVKTag, 0x2b2ddc0b); + EXPECT_EQ(m_1h_2h_3h.childIndex, 3 | HARDENED_KEY_LIMIT); EXPECT_EQ( - m_1_2hv_3.chaincode, - uint256S("e8e7d6a74a5a1c05be41baec7998d91f7b3603a4c0af495b0d43ba81cf7b938d")); + m_1h_2h_3h.chaincode, + uint256S("4bc7bd5b38fcbdb259be013bef298c8de263e4c32ccb2bcdd2ce90762d01dc33")); EXPECT_EQ( - m_1_2hv_3.fvk.ak, - uint256S("a3a697bdda9d648d32a97553de4754b2fac866d726d3f2c436259c507bc585b1")); + m_1h_2h_3h.expsk.ask, + uint256S("07afec4df829ace083d68145103c50692f331c4690cf52f13759e3214dd29345")); EXPECT_EQ( - m_1_2hv_3.fvk.nk, - uint256S("4f66c0814b769963f3bf1bc001270b50edabb27de042fc8a5607d2029e0488db")); + m_1h_2h_3h.expsk.nsk, + uint256S("0bbba7ff6efab6fbabb5b727ed3d55e40efa0de858f8c0e357503f12c27ec81a")); EXPECT_EQ( - m_1_2hv_3.fvk.ovk, - uint256S("f61a699934dc78441324ef628b4b4721611571e8ee3bd591eb3d4b1cfae0b969")); + m_1h_2h_3h.expsk.ovk, + uint256S("e39e4b1b9c5c1b31988beffb515522a95de718afa880e36c9d2ebef20cea361e")); EXPECT_EQ( - m_1_2hv_3.dk, - uint256S("6ee53b1261f2c9c0f7359ab236f87b52a0f1b0ce43305cdad92ebb63c350cbbe")); + m_1h_2h_3h.dk, + uint256S("6abb7b109c7270edaa3a36dc140d3f70bf8cd271b69d606f5aadf3a4596cfc57")); EXPECT_THAT( - m_1_2hv_3.DefaultAddress().d, - testing::ElementsAreArray({ 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd })); + m_1h_2h_3h.ToXFVK().DefaultAddress().d, + testing::ElementsAreArray({ 0x5a, 0x75, 0xbe, 0x14, 0x00, 0x53, 0x0b, 0x4b, 0x7a, 0xdd, 0x52 })); } TEST(ZIP32, ParseHDKeypathAccount) { diff --git a/src/gtest/utils.cpp b/src/gtest/utils.cpp index dee99399c8..f4342af1bf 100644 --- a/src/gtest/utils.cpp +++ b/src/gtest/utils.cpp @@ -79,9 +79,15 @@ template<> void AppendRandomLeaf(OrchardMerkleFrontier &tree) { // fortunately the tests only require that the tree root change. // TODO: Remove the need to create proofs by having a testing-only way to // append a random leaf to OrchardMerkleFrontier. + RawHDSeed seed(32, 0); + auto to = libzcash::OrchardSpendingKey::ForAccount(seed, 133, 0) + .ToFullViewingKey() + .GetChangeAddress(); uint256 orchardAnchor; uint256 dataToBeSigned; - auto builder = orchard::Builder(true, true, orchardAnchor); + // TODO: Create bundle. + auto builder = orchard::Builder(false, orchardAnchor); + builder.AddOutput(std::nullopt, to, 0, std::nullopt); auto bundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value(); tree.AppendBundle(bundle); } diff --git a/src/miner.cpp b/src/miner.cpp index 5c56d5d524..4c9e4250d7 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -173,7 +173,7 @@ class AddOutputsToCoinbaseTxAndSign void ComputeBindingSig(rust::Box saplingBuilder, std::optional orchardBundle) const { auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); - auto saplingBundle = sapling::build_bundle(std::move(saplingBuilder), nHeight); + auto saplingBundle = sapling::build_bundle(std::move(saplingBuilder)); // Empty output script. uint256 dataToBeSigned; @@ -213,13 +213,14 @@ class AddOutputsToCoinbaseTxAndSign // Create Orchard output void operator()(const libzcash::OrchardRawAddress &to) const { - auto saplingBuilder = sapling::new_builder(*chainparams.RustNetwork(), nHeight); + std::array saplingAnchor; + auto saplingBuilder = sapling::new_builder(*chainparams.RustNetwork(), nHeight, saplingAnchor, true); // `enableSpends` must be set to `false` for coinbase transactions. This // means the Orchard anchor is unconstrained, so we set it to the empty // tree root via a null (all zeroes) uint256. uint256 orchardAnchor; - auto builder = orchard::Builder(false, true, orchardAnchor); + auto builder = orchard::Builder(true, orchardAnchor); // Shielded coinbase outputs must be recoverable with an all-zeroes ovk. uint256 ovk; @@ -249,7 +250,8 @@ class AddOutputsToCoinbaseTxAndSign // Create Sapling output void operator()(const libzcash::SaplingPaymentAddress &pa) const { - auto saplingBuilder = sapling::new_builder(*chainparams.RustNetwork(), nHeight); + std::array saplingAnchor; + auto saplingBuilder = sapling::new_builder(*chainparams.RustNetwork(), nHeight, saplingAnchor, true); auto miner_reward = SetFoundersRewardAndGetMinerValue(*saplingBuilder); @@ -265,7 +267,8 @@ class AddOutputsToCoinbaseTxAndSign // Create transparent output void operator()(const boost::shared_ptr &coinbaseScript) const { // Add the FR output and fetch the miner's output value. - auto saplingBuilder = sapling::new_builder(*chainparams.RustNetwork(), nHeight); + std::array saplingAnchor; + auto saplingBuilder = sapling::new_builder(*chainparams.RustNetwork(), nHeight, saplingAnchor, true); // Miner output will be vout[0]; Founders' Reward & funding stream outputs // will follow. diff --git a/src/rust/bin/inspect/context.rs b/src/rust/bin/inspect/context.rs index 0bd240d0e3..f7d9a56dae 100644 --- a/src/rust/bin/inspect/context.rs +++ b/src/rust/bin/inspect/context.rs @@ -9,7 +9,7 @@ use serde::{ use zcash_primitives::{ consensus::Network, legacy::Script, - transaction::components::{transparent, Amount, TxOut}, + transaction::components::{amount::NonNegativeAmount, transparent, TxOut}, zip32::AccountId, }; @@ -67,7 +67,9 @@ impl<'de> Visitor<'de> for JsonAccountIdVisitor { { u32::try_from(v) .map_err(|_| E::custom(format!("u32 out of range: {}", v))) - .map(AccountId::from) + .and_then(|a| { + AccountId::try_from(a).map_err(|e| E::custom(format!("AccountId invalid: {}", e))) + }) .map(JsonAccountId) } @@ -77,7 +79,9 @@ impl<'de> Visitor<'de> for JsonAccountIdVisitor { { u32::try_from(v) .map_err(|_| E::custom(format!("u32 out of range: {}", v))) - .map(AccountId::from) + .and_then(|a| { + AccountId::try_from(a).map_err(|e| E::custom(format!("AccountId invalid: {}", e))) + }) .map(JsonAccountId) } @@ -87,7 +91,9 @@ impl<'de> Visitor<'de> for JsonAccountIdVisitor { { u32::try_from(v) .map_err(|_| E::custom(format!("u32 out of range: {}", v))) - .map(AccountId::from) + .and_then(|a| { + AccountId::try_from(a).map_err(|e| E::custom(format!("AccountId invalid: {}", e))) + }) .map(JsonAccountId) } @@ -97,7 +103,9 @@ impl<'de> Visitor<'de> for JsonAccountIdVisitor { { u32::try_from(v) .map_err(|_| E::custom(format!("u32 out of range: {}", v))) - .map(AccountId::from) + .and_then(|a| { + AccountId::try_from(a).map_err(|e| E::custom(format!("AccountId invalid: {}", e))) + }) .map(JsonAccountId) } } @@ -162,7 +170,7 @@ impl fmt::Display for ZUint256 { } #[derive(Clone, Debug)] -struct ZOutputValue(Amount); +struct ZOutputValue(NonNegativeAmount); struct ZOutputValueVisitor; @@ -177,9 +185,11 @@ impl<'de> Visitor<'de> for ZOutputValueVisitor { where E: serde::de::Error, { - Amount::from_u64(v).map(ZOutputValue).map_err(|()| { - serde::de::Error::invalid_type(Unexpected::Unsigned(v), &"a valid zatoshi amount") - }) + NonNegativeAmount::from_u64(v) + .map(ZOutputValue) + .map_err(|()| { + serde::de::Error::invalid_type(Unexpected::Unsigned(v), &"a valid zatoshi amount") + }) } } diff --git a/src/rust/bin/inspect/keys.rs b/src/rust/bin/inspect/keys.rs index 152f95cfdc..a0b188e452 100644 --- a/src/rust/bin/inspect/keys.rs +++ b/src/rust/bin/inspect/keys.rs @@ -36,7 +36,7 @@ pub(crate) fn inspect_mnemonic( let orchard_fvk = match orchard::keys::SpendingKey::from_zip32_seed( &seed, network.coin_type(), - account.into(), + account, ) { Ok(sk) => Some(orchard::keys::FullViewingKey::from(&sk)), Err(e) => { @@ -49,13 +49,13 @@ pub(crate) fn inspect_mnemonic( }; eprintln!(" - Sapling:"); - let sapling_master = zip32::ExtendedSpendingKey::master(&seed); - let sapling_extsk = zip32::ExtendedSpendingKey::from_path( + let sapling_master = sapling::zip32::ExtendedSpendingKey::master(&seed); + let sapling_extsk = sapling::zip32::ExtendedSpendingKey::from_path( &sapling_master, &[ - zip32::ChildIndex::Hardened(32), - zip32::ChildIndex::Hardened(network.coin_type()), - zip32::ChildIndex::Hardened(account.into()), + zip32::ChildIndex::hardened(32), + zip32::ChildIndex::hardened(network.coin_type()), + account.into(), ], ); #[allow(deprecated)] @@ -106,8 +106,8 @@ pub(crate) fn inspect_mnemonic( Ok(addr) => eprintln!( " - Default address: {}", match addr { - TransparentAddress::PublicKey(data) => ZcashAddress::from_transparent_p2pkh(addr_net, data), - TransparentAddress::Script(_) => unreachable!(), + TransparentAddress::PublicKeyHash(data) => ZcashAddress::from_transparent_p2pkh(addr_net, data), + TransparentAddress::ScriptHash(_) => unreachable!(), }.encode(), ), Err(e) => eprintln!( diff --git a/src/rust/bin/inspect/main.rs b/src/rust/bin/inspect/main.rs index e36ef89571..158d6ba07c 100644 --- a/src/rust/bin/inspect/main.rs +++ b/src/rust/bin/inspect/main.rs @@ -98,8 +98,9 @@ fn inspect_bytes(bytes: Vec, context: Option) { block::inspect(&block, context); } else if let Some(header) = complete(&bytes, |r| BlockHeader::read(r)) { block::inspect_header(&header, context); - } else if let Some(tx) = complete(&bytes, |r| Transaction::read(r, BranchId::Sprout)) { + } else if let Some(tx) = complete(&bytes, |r| Transaction::read(r, BranchId::Nu5)) { // TODO: Take the branch ID used above from the context if present. + // https://github.com/zcash/zcash/issues/6831 transaction::inspect(tx, context); } else { // It's not a known variable-length format. check fixed-length data formats. diff --git a/src/rust/bin/inspect/transaction.rs b/src/rust/bin/inspect/transaction.rs index 56af769cd6..4e9308c00b 100644 --- a/src/rust/bin/inspect/transaction.rs +++ b/src/rust/bin/inspect/transaction.rs @@ -6,6 +6,7 @@ use std::{ use bellman::groth16; use group::GroupEncoding; use orchard::note_encryption::OrchardDomain; +use sapling::{note_encryption::SaplingDomain, SaplingVerificationContext}; use secp256k1::{Secp256k1, VerifyOnly}; use zcash_address::{ unified::{self, Encoding}, @@ -14,18 +15,16 @@ use zcash_address::{ use zcash_note_encryption::try_output_recovery_with_ovk; #[allow(deprecated)] use zcash_primitives::{ - consensus::BlockHeight, + consensus::{sapling_zip212_enforcement, BlockHeight}, legacy::{keys::pubkey_to_address, Script, TransparentAddress}, memo::{Memo, MemoBytes}, - sapling::note_encryption::SaplingDomain, transaction::{ - components::{sapling, transparent, Amount}, + components::{amount::NonNegativeAmount, transparent}, sighash::{signature_hash, SignableInput, TransparentAuthorizingContext}, txid::TxIdDigester, Authorization, Transaction, TransactionData, TxId, TxVersion, }, }; -use zcash_proofs::sapling::SaplingVerificationContext; use crate::{ context::{Context, ZTxOut}, @@ -106,7 +105,7 @@ impl transparent::Authorization for TransparentAuth { } impl TransparentAuthorizingContext for TransparentAuth { - fn input_amounts(&self) -> Vec { + fn input_amounts(&self) -> Vec { self.all_prev_outputs .iter() .map(|prevout| prevout.value) @@ -143,7 +142,7 @@ pub(crate) struct PrecomputedAuth; impl Authorization for PrecomputedAuth { type TransparentAuth = TransparentAuth; - type SaplingAuth = sapling::Authorized; + type SaplingAuth = sapling::bundle::Authorized; type OrchardAuth = orchard::bundle::Authorized; } @@ -231,7 +230,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { txin.prevout.n() ); match coin.recipient_address() { - Some(addr @ TransparentAddress::PublicKey(_)) => { + Some(addr @ TransparentAddress::PublicKeyHash(_)) => { // Format is [sig_and_type_len] || sig || [hash_type] || [pubkey_len] || pubkey // where [x] encodes a single byte. let sig_and_type_len = txin.script_sig.0.first().map(|l| *l as usize); @@ -311,7 +310,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { } } // TODO: Check P2SH structure. - Some(TransparentAddress::Script(_)) => { + Some(TransparentAddress::ScriptHash(_)) => { eprintln!(" 🔎 \"transparentcoins\"[{}] is a P2SH coin.", i); } // TODO: Check arbitrary scripts. @@ -376,7 +375,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { assert!(!(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty())); // TODO: Separate into checking proofs, signatures, and other structural details. - let mut ctx = SaplingVerificationContext::new(true); + let mut ctx = SaplingVerificationContext::new(); if !bundle.shielded_spends().is_empty() { eprintln!(" - {} Sapling Spend(s)", bundle.shielded_spends().len()); @@ -386,7 +385,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { spend.cv(), *spend.anchor(), &spend.nullifier().0, - spend.rk().clone(), + *spend.rk(), sighash.as_ref(), *spend.spend_auth_sig(), groth16::Proof::read(&spend.zkproof()[..]).unwrap(), @@ -411,8 +410,11 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { .and_then(|ctx| ctx.network().zip(ctx.addr_network())) { if let Some((note, addr, memo)) = try_output_recovery_with_ovk( - &SaplingDomain::for_height(params, height.unwrap()), - &zcash_primitives::keys::OutgoingViewingKey([0; 32]), + &SaplingDomain::new(sapling_zip212_enforcement( + ¶ms, + height.unwrap(), + )), + &sapling::keys::OutgoingViewingKey([0; 32]), output, output.cv(), output.out_ciphertext(), @@ -426,6 +428,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { eprintln!(" - {}", zaddr); eprintln!(" - {}", render_value(note.value().inner())); } + let memo = MemoBytes::from_bytes(&memo).expect("correct length"); eprintln!(" - {}", render_memo(memo)); } else { eprintln!( diff --git a/src/rust/include/rust/builder.h b/src/rust/include/rust/builder.h index 8e16782151..716841452b 100644 --- a/src/rust/include/rust/builder.h +++ b/src/rust/include/rust/builder.h @@ -35,8 +35,7 @@ void orchard_spend_info_free(OrchardSpendInfoPtr* ptr); /// /// If `anchor` is `null`, the root of the empty Orchard commitment tree is used. OrchardBuilderPtr* orchard_builder_new( - bool spends_enabled, - bool outputs_enabled, + bool coinbase, const unsigned char* anchor); /// Frees an Orchard builder returned from `orchard_builder_new`. diff --git a/src/rust/src/address_ffi.rs b/src/rust/src/address_ffi.rs index c05da6f87d..a7adda677b 100644 --- a/src/rust/src/address_ffi.rs +++ b/src/rust/src/address_ffi.rs @@ -9,7 +9,6 @@ use zcash_address::{ unified::{self, Container, Encoding}, Network, ToAddress, TryFromAddress, ZcashAddress, }; -use zcash_primitives::sapling; pub type UnifiedAddressObj = NonNull; pub type AddOrchardReceiverCb = diff --git a/src/rust/src/bridge.rs b/src/rust/src/bridge.rs index 6cf15095fe..268e90501f 100644 --- a/src/rust/src/bridge.rs +++ b/src/rust/src/bridge.rs @@ -185,11 +185,15 @@ pub(crate) mod ffi { type SaplingBuilder; #[cxx_name = "new_builder"] - fn new_sapling_builder(network: &Network, height: u32) -> Box; + fn new_sapling_builder( + network: &Network, + height: u32, + anchor: [u8; 32], + coinbase: bool, + ) -> Result>; fn add_spend( self: &mut SaplingBuilder, extsk: &[u8], - diversifier: [u8; 11], recipient: [u8; 43], value: u64, rcm: [u8; 32], @@ -205,7 +209,6 @@ pub(crate) mod ffi { #[cxx_name = "build_bundle"] fn build_sapling_bundle( builder: Box, - target_height: u32, ) -> Result>; #[cxx_name = "UnauthorizedBundle"] diff --git a/src/rust/src/builder_ffi.rs b/src/rust/src/builder_ffi.rs index cd9b72c7df..10d9b1fc14 100644 --- a/src/rust/src/builder_ffi.rs +++ b/src/rust/src/builder_ffi.rs @@ -6,8 +6,8 @@ use incrementalmerkletree::Hashable; use libc::size_t; use orchard::keys::SpendingKey; use orchard::{ - builder::{Builder, InProgress, Unauthorized, Unproven}, - bundle::{Authorized, Flags}, + builder::{Builder, BundleType, InProgress, Unauthorized, Unproven}, + bundle::Authorized, keys::{FullViewingKey, OutgoingViewingKey}, tree::{MerkleHashOrchard, MerklePath}, value::NoteValue, @@ -18,7 +18,7 @@ use tracing::error; use zcash_primitives::{ consensus::BranchId, transaction::{ - components::{sapling, Amount}, + components::Amount, sighash::{signature_hash, SignableInput}, txid::TxIdDigester, Authorization, Transaction, TransactionData, @@ -55,18 +55,16 @@ pub extern "C" fn orchard_spend_info_free(spend_info: *mut OrchardSpendInfo) { } #[no_mangle] -pub extern "C" fn orchard_builder_new( - spends_enabled: bool, - outputs_enabled: bool, - anchor: *const [u8; 32], -) -> *mut Builder { +pub extern "C" fn orchard_builder_new(coinbase: bool, anchor: *const [u8; 32]) -> *mut Builder { + let bundle_type = if coinbase { + BundleType::Coinbase + } else { + BundleType::DEFAULT + }; let anchor = unsafe { anchor.as_ref() } .map(|a| orchard::Anchor::from_bytes(*a).unwrap()) .unwrap_or_else(|| MerkleHashOrchard::empty_root(32.into()).into()); - Box::into_raw(Box::new(Builder::new( - Flags::from_parts(spends_enabled, outputs_enabled), - anchor, - ))) + Box::into_raw(Box::new(Builder::new(bundle_type, anchor))) } #[no_mangle] @@ -106,7 +104,7 @@ pub extern "C" fn orchard_builder_add_recipient( let value = NoteValue::from_raw(value); let memo = unsafe { memo.as_ref() }.copied(); - match builder.add_recipient(ovk, *recipient, value, memo) { + match builder.add_output(ovk, *recipient, value, memo) { Ok(()) => true, Err(e) => { error!("Failed to add Orchard recipient: {}", e); @@ -132,8 +130,15 @@ pub extern "C" fn orchard_builder_build( } let builder = unsafe { Box::from_raw(builder) }; - match builder.build(OsRng) { - Ok(bundle) => Box::into_raw(Box::new(bundle)), + match builder.build::(OsRng) { + Ok(Some((bundle, _))) => Box::into_raw(Box::new(bundle)), + Ok(None) => { + // The C++ side only calls `orchard_builder_build` when it expects the + // resulting bundle to be non-empty (either at least one Orchard output for + // coinbase transactions, or a potentially-empty bundle that gets padded). + error!("Tried to build empty Orchard bundle"); + ptr::null_mut() + } Err(e) => { error!("Failed to build Orchard bundle: {:?}", e); ptr::null_mut() @@ -232,7 +237,8 @@ pub(crate) fn shielded_signature_digest( struct Signable {} impl Authorization for Signable { type TransparentAuth = TransparentAuth; - type SaplingAuth = sapling::builder::Unauthorized; + type SaplingAuth = + sapling::builder::InProgress; type OrchardAuth = InProgress; } diff --git a/src/rust/src/incremental_merkle_tree.rs b/src/rust/src/incremental_merkle_tree.rs index df22496d12..10d2205b8e 100644 --- a/src/rust/src/incremental_merkle_tree.rs +++ b/src/rust/src/incremental_merkle_tree.rs @@ -5,12 +5,9 @@ use std::io::{self, Read, Write}; use bridgetree::{BridgeTree, Checkpoint, MerkleBridge}; use incrementalmerkletree::{Address, Hashable, Level, Position}; use zcash_encoding::{Optional, Vector}; -use zcash_primitives::{ - merkle_tree::{ - read_address, read_leu64_usize, read_nonempty_frontier_v1, read_position, write_address, - write_nonempty_frontier_v1, write_position, write_usize_leu64, HashSer, - }, - sapling::NOTE_COMMITMENT_TREE_DEPTH, +use zcash_primitives::merkle_tree::{ + read_address, read_leu64_usize, read_nonempty_frontier_v1, read_position, write_address, + write_nonempty_frontier_v1, write_position, write_usize_leu64, HashSer, }; pub const SER_V1: u8 = 1; @@ -252,9 +249,9 @@ pub fn write_checkpoint_v3( /// ids should always be treated as opaque, totally ordered identifiers without additional /// semantics. #[allow(clippy::redundant_closure)] -pub fn read_tree( +pub fn read_tree( mut reader: R, -) -> io::Result> { +) -> io::Result> { let tree_version = reader.read_u8()?; let prior_bridges = Vector::read(&mut reader, |r| read_bridge(r, tree_version))?; let current_bridge = Optional::read(&mut reader, |r| read_bridge(r, tree_version))?; @@ -309,9 +306,9 @@ pub fn read_tree( }) } -pub fn write_tree( +pub fn write_tree( mut writer: W, - tree: &BridgeTree, + tree: &BridgeTree, ) -> io::Result<()> { writer.write_u8(SER_V3)?; Vector::write(&mut writer, tree.prior_bridges(), |w, b| write_bridge(w, b))?; diff --git a/src/rust/src/init.rs b/src/rust/src/init.rs index 67de7e9773..f7a800daf0 100644 --- a/src/rust/src/init.rs +++ b/src/rust/src/init.rs @@ -2,8 +2,8 @@ use std::ffi::OsString; use std::path::PathBuf; use std::sync::Once; -use bellman::groth16::Parameters; use bls12_381::Bls12; +use sapling::circuit::{OutputParameters, SpendParameters}; use tracing::info; use crate::{ @@ -51,17 +51,18 @@ fn zksnark_params(sprout_path: String, load_proving_keys: bool) { // Load params let (sapling_spend_params, sapling_output_params) = { let (spend_buf, output_buf) = wagyu_zcash_parameters::load_sapling_parameters(); - let spend_params = Parameters::::read(&spend_buf[..], false) - .expect("couldn't deserialize Sapling spend parameters"); - let output_params = Parameters::::read(&output_buf[..], false) - .expect("couldn't deserialize Sapling spend parameters"); - (spend_params, output_params) + ( + SpendParameters::read(&spend_buf[..], false) + .expect("Failed to read Sapling Spend parameters"), + OutputParameters::read(&output_buf[..], false) + .expect("Failed to read Sapling Output parameters"), + ) }; // We need to clone these because we aren't necessarily storing the proving // parameters in memory. - let sapling_spend_vk = sapling_spend_params.vk.clone(); - let sapling_output_vk = sapling_output_params.vk.clone(); + let sapling_spend_vk = sapling_spend_params.verifying_key(); + let sapling_output_vk = sapling_output_params.verifying_key(); // Generate Orchard parameters. info!(target: "main", "Loading Orchard parameters"); diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 3c77c26437..b9d9b103ba 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -19,7 +19,10 @@ // See https://github.com/rust-lang/rfcs/pull/2585 for more background. #![allow(clippy::not_unsafe_ptr_arg_deref)] -use bellman::groth16::{self, Parameters, PreparedVerifyingKey}; +use ::sapling::circuit::{ + OutputParameters, OutputVerifyingKey, SpendParameters, SpendVerifyingKey, +}; +use bellman::groth16::PreparedVerifyingKey; use bls12_381::Bls12; use std::path::PathBuf; use subtle::CtOption; @@ -61,12 +64,12 @@ mod test_harness_ffi; #[cfg(test)] mod tests; -static mut SAPLING_SPEND_VK: Option> = None; -static mut SAPLING_OUTPUT_VK: Option> = None; +static mut SAPLING_SPEND_VK: Option = None; +static mut SAPLING_OUTPUT_VK: Option = None; static mut SPROUT_GROTH16_VK: Option> = None; -static mut SAPLING_SPEND_PARAMS: Option> = None; -static mut SAPLING_OUTPUT_PARAMS: Option> = None; +static mut SAPLING_SPEND_PARAMS: Option = None; +static mut SAPLING_OUTPUT_PARAMS: Option = None; static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; static mut ORCHARD_PK: Option = None; diff --git a/src/rust/src/merkle_frontier.rs b/src/rust/src/merkle_frontier.rs index ae4b3b7d71..d53a08213b 100644 --- a/src/rust/src/merkle_frontier.rs +++ b/src/rust/src/merkle_frontier.rs @@ -5,9 +5,9 @@ use incrementalmerkletree::{ Hashable, Level, }; use orchard::tree::MerkleHashOrchard; -use zcash_primitives::{ - merkle_tree::{read_frontier_v1, write_commitment_tree, write_frontier_v1, HashSer}, - sapling::NOTE_COMMITMENT_TREE_DEPTH, +use sapling::NOTE_COMMITMENT_TREE_DEPTH; +use zcash_primitives::merkle_tree::{ + read_frontier_v1, write_commitment_tree, write_frontier_v1, HashSer, }; use crate::{bridge::ffi, orchard_bundle, streams::CppStream, wallet::Wallet}; diff --git a/src/rust/src/note_encryption.rs b/src/rust/src/note_encryption.rs index 069fc712c2..5806bf68fc 100644 --- a/src/rust/src/note_encryption.rs +++ b/src/rust/src/note_encryption.rs @@ -1,19 +1,15 @@ use std::convert::TryInto; +use sapling::{ + keys::OutgoingViewingKey, + note_encryption::{PreparedIncomingViewingKey, SaplingDomain}, + value::ValueCommitment, + SaplingIvk, +}; use zcash_note_encryption::{ try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, ShieldedOutput, ENC_CIPHERTEXT_SIZE, }; -use zcash_primitives::{ - consensus::BlockHeight, - keys::OutgoingViewingKey, - memo::MemoBytes, - sapling::{ - self, - note_encryption::{PreparedIncomingViewingKey, SaplingDomain}, - value::ValueCommitment, - SaplingIvk, - }, -}; +use zcash_primitives::consensus::{sapling_zip212_enforcement, BlockHeight}; use crate::{bridge::ffi::SaplingShieldedOutput, params::Network}; @@ -35,10 +31,9 @@ pub(crate) fn try_sapling_note_decryption( .ok_or("Invalid Sapling ivk passed to wallet::try_sapling_note_decryption()")?; let (note, recipient, memo) = sapling::note_encryption::try_sapling_note_decryption( - network, - BlockHeight::from_u32(height), &ivk, &output, + sapling_zip212_enforcement(network, BlockHeight::from_u32(height)), ) .ok_or("Decryption failed")?; @@ -64,7 +59,10 @@ pub(crate) fn try_sapling_output_recovery( ovk: [u8; 32], output: SaplingShieldedOutput, ) -> Result, &'static str> { - let domain = SaplingDomain::for_height(*network, BlockHeight::from_u32(height)); + let domain = SaplingDomain::new(sapling_zip212_enforcement( + network, + BlockHeight::from_u32(height), + )); let cv = Option::from(ValueCommitment::from_bytes_not_small_order(&output.cv)) .ok_or("Invalid output.cv passed to wallet::try_sapling_note_decryption()")?; @@ -94,12 +92,12 @@ pub(crate) fn parse_and_prepare_sapling_ivk( .into() } -impl ShieldedOutput, ENC_CIPHERTEXT_SIZE> for SaplingShieldedOutput { +impl ShieldedOutput for SaplingShieldedOutput { fn ephemeral_key(&self) -> EphemeralKeyBytes { EphemeralKeyBytes(self.ephemeral_key) } - fn cmstar_bytes(&self) -> as Domain>::ExtractedCommitmentBytes { + fn cmstar_bytes(&self) -> ::ExtractedCommitmentBytes { self.cmu } @@ -112,7 +110,7 @@ impl ShieldedOutput, ENC_CIPHERTEXT_SIZE> for SaplingShie pub(crate) struct DecryptedSaplingOutput { note: sapling::Note, recipient: sapling::PaymentAddress, - memo: MemoBytes, + memo: [u8; 512], } impl DecryptedSaplingOutput { @@ -140,6 +138,6 @@ impl DecryptedSaplingOutput { } pub(crate) fn memo(&self) -> [u8; 512] { - *self.memo.as_array() + self.memo } } diff --git a/src/rust/src/orchard_keys_ffi.rs b/src/rust/src/orchard_keys_ffi.rs index f992a3759d..87a551ba30 100644 --- a/src/rust/src/orchard_keys_ffi.rs +++ b/src/rust/src/orchard_keys_ffi.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::io::{Read, Write}; use std::slice; use tracing::error; @@ -160,7 +161,7 @@ pub extern "C" fn orchard_incoming_viewing_key_decrypt_diversifier( match key.diversifier_index(addr) { Some(j) => { - j_ret.copy_from_slice(j.to_bytes()); + j_ret.copy_from_slice(j.as_bytes()); true } None => false, @@ -327,7 +328,8 @@ pub extern "C" fn orchard_spending_key_for_account( account_id: u32, ) -> *mut SpendingKey { let seed = unsafe { slice::from_raw_parts(seed, seed_len) }; - SpendingKey::from_zip32_seed(seed, bip44_coin_type, account_id) + let account = account_id.try_into().expect("account_id should be a u31"); + SpendingKey::from_zip32_seed(seed, bip44_coin_type, account) .map(|key| Box::into_raw(Box::new(key))) .unwrap_or(std::ptr::null_mut()) } diff --git a/src/rust/src/sapling.rs b/src/rust/src/sapling.rs index 895d7ab149..487348487f 100644 --- a/src/rust/src/sapling.rs +++ b/src/rust/src/sapling.rs @@ -2,37 +2,35 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . -use std::convert::TryInto; -use std::io::{self, Read, Write}; +use std::convert::{TryFrom, TryInto}; +use std::io; use std::mem; -use bellman::groth16::{prepare_verifying_key, Proof}; +use bellman::groth16::Proof; +use bls12_381::Bls12; use group::GroupEncoding; -use incrementalmerkletree::MerklePath; use memuse::DynamicUsage; -use rand_core::OsRng; -use zcash_encoding::Vector; +use rand_core::{OsRng, RngCore}; +use sapling::{ + builder::BundleType, + circuit::{self, OutputParameters, SpendParameters}, + keys::{OutgoingViewingKey, SpendAuthorizingKey}, + note::ExtractedNoteCommitment, + prover::{OutputProver, SpendProver}, + value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, + zip32::ExtendedSpendingKey, + Anchor, Diversifier, MerklePath, Note, PaymentAddress, ProofGenerationKey, Rseed, + SaplingVerificationContext, +}; use zcash_primitives::{ - keys::OutgoingViewingKey, + consensus::sapling_zip212_enforcement, memo::MemoBytes, merkle_tree::merkle_path_from_slice, - sapling::{ - note::ExtractedNoteCommitment, - prover::TxProver, - redjubjub::{self, Signature}, - value::{NoteValue, ValueCommitment}, - Diversifier, Node, Note, PaymentAddress, ProofGenerationKey, Rseed, - NOTE_COMMITMENT_TREE_DEPTH, - }, transaction::{ - components::{sapling, Amount}, + components::{sapling as sapling_serialization, Amount}, txid::{BlockTxCommitmentDigester, TxIdDigester}, Authorized, Transaction, TransactionDigest, }, - zip32::ExtendedSpendingKey, -}; -use zcash_proofs::sapling::{ - self as sapling_proofs, SaplingProvingContext, SaplingVerificationContext, }; use super::GROTH_PROOF_SIZE; @@ -50,10 +48,10 @@ mod zip32; const SAPLING_TREE_DEPTH: usize = 32; -pub(crate) struct Spend(sapling::SpendDescription); +pub(crate) struct Spend(sapling::bundle::SpendDescription); pub(crate) fn parse_v4_sapling_spend(bytes: &[u8]) -> Result, String> { - sapling::SpendDescription::read(&mut io::Cursor::new(bytes)) + sapling_serialization::temporary_zcashd_read_spend_v4(&mut io::Cursor::new(bytes)) .map(Spend) .map(Box::new) .map_err(|e| format!("{}", e)) @@ -73,7 +71,7 @@ impl Spend { } pub(crate) fn rk(&self) -> [u8; 32] { - self.0.rk().0.to_bytes() + (*self.0.rk()).into() } pub(crate) fn zkproof(&self) -> [u8; 192] { @@ -81,19 +79,14 @@ impl Spend { } pub(crate) fn spend_auth_sig(&self) -> [u8; 64] { - let mut ret = [0; 64]; - self.0 - .spend_auth_sig() - .write(&mut ret[..]) - .expect("correct length"); - ret + (*self.0.spend_auth_sig()).into() } } -pub(crate) struct Output(sapling::OutputDescription<[u8; 192]>); +pub(crate) struct Output(sapling::bundle::OutputDescription<[u8; 192]>); pub(crate) fn parse_v4_sapling_output(bytes: &[u8]) -> Result, String> { - sapling::OutputDescription::read(&mut io::Cursor::new(bytes)) + sapling_serialization::temporary_zcashd_read_output_v4(&mut io::Cursor::new(bytes)) .map(Output) .map(Box::new) .map_err(|e| format!("{}", e)) @@ -125,14 +118,13 @@ impl Output { } pub(crate) fn serialize_v4(&self, writer: &mut CppStream<'_>) -> Result<(), String> { - self.0 - .write_v4(writer) + sapling_serialization::temporary_zcashd_write_output_v4(writer, &self.0) .map_err(|e| format!("Failed to write v4 Sapling Output Description: {}", e)) } } #[derive(Clone)] -pub(crate) struct Bundle(pub(crate) Option>); +pub(crate) struct Bundle(pub(crate) Option>); pub(crate) fn none_sapling_bundle() -> Box { Box::new(Bundle(None)) @@ -159,37 +151,15 @@ impl Bundle { pub(crate) fn serialize_v4_components( &self, - mut writer: &mut CppStream<'_>, + writer: &mut CppStream<'_>, has_sapling: bool, ) -> Result<(), String> { - if has_sapling { - let mut write_v4 = || { - writer.write_all( - &self - .0 - .as_ref() - .map_or(Amount::zero(), |b| *b.value_balance()) - .to_i64_le_bytes(), - )?; - Vector::write( - &mut writer, - self.0.as_ref().map_or(&[], |b| b.shielded_spends()), - |w, e| e.write_v4(w), - )?; - Vector::write( - &mut writer, - self.0.as_ref().map_or(&[], |b| b.shielded_outputs()), - |w, e| e.write_v4(w), - ) - }; - write_v4().map_err(|e| format!("{}", e))?; - } else if self.0.is_some() { - return Err( - "Sapling components may not be present if Sapling is not active.".to_string(), - ); - } - - Ok(()) + sapling_serialization::temporary_zcashd_write_v4_components( + writer, + self.0.as_ref(), + has_sapling, + ) + .map_err(|e| format!("{}", e)) } /// Serializes an authorized Sapling bundle to the given stream. @@ -200,7 +170,7 @@ impl Bundle { .map_err(|e| format!("Failed to serialize Sapling bundle: {}", e)) } - pub(crate) fn inner(&self) -> Option<&sapling::Bundle> { + pub(crate) fn inner(&self) -> Option<&sapling::Bundle> { self.0.as_ref() } @@ -264,14 +234,11 @@ impl Bundle { /// /// Panics if the bundle is not present. pub(crate) fn binding_sig(&self) -> [u8; 64] { - let mut ret = [0; 64]; self.inner() .expect("Bundle actions should have been checked to be non-empty") .authorization() .binding_sig - .write(&mut ret[..]) - .expect("correct length"); - ret + .into() } fn commitment>(&self, digester: D) -> D::SaplingDigest { @@ -281,8 +248,8 @@ impl Bundle { pub(crate) struct BundleAssembler { value_balance: Amount, - shielded_spends: Vec>, - shielded_outputs: Vec>, // GROTH_PROOF_SIZE + shielded_spends: Vec>, + shielded_outputs: Vec>, // GROTH_PROOF_SIZE } pub(crate) fn new_bundle_assembler() -> Box { @@ -303,27 +270,11 @@ pub(crate) fn parse_v4_sapling_components( impl BundleAssembler { pub(crate) fn parse_v4_components( - mut reader: &mut CppStream<'_>, + reader: &mut CppStream<'_>, has_sapling: bool, ) -> io::Result> { - let (value_balance, shielded_spends, shielded_outputs) = if has_sapling { - let vb = { - let mut tmp = [0; 8]; - reader.read_exact(&mut tmp)?; - Amount::from_i64_le_bytes(tmp).map_err(|_| { - io::Error::new(io::ErrorKind::InvalidData, "valueBalance out of range") - })? - }; - #[allow(clippy::redundant_closure)] - let ss: Vec> = - Vector::read(&mut reader, |r| sapling::SpendDescription::read(r))?; - #[allow(clippy::redundant_closure)] - let so: Vec> = - Vector::read(&mut reader, |r| sapling::OutputDescription::read(r))?; - (vb, ss, so) - } else { - (Amount::zero(), vec![], vec![]) - }; + let (value_balance, shielded_spends, shielded_outputs) = + sapling_serialization::temporary_zcashd_read_v4_components(reader, has_sapling)?; Ok(Box::new(Self { value_balance, @@ -342,125 +293,119 @@ pub(crate) fn finish_bundle_assembly( assembler: Box, binding_sig: [u8; 64], ) -> Box { - let binding_sig = redjubjub::Signature::read(&binding_sig[..]).expect("parsed elsewhere"); + let binding_sig = redjubjub::Signature::from(binding_sig); - Box::new(Bundle(Some(sapling::Bundle::temporary_zcashd_from_parts( + Box::new(Bundle(sapling::Bundle::from_parts( assembler.shielded_spends, assembler.shielded_outputs, assembler.value_balance, - sapling::Authorized { binding_sig }, - )))) + sapling::bundle::Authorized { binding_sig }, + ))) } pub(crate) struct StaticTxProver; -impl TxProver for StaticTxProver { - type SaplingProvingContext = SaplingProvingContext; - - fn new_sapling_proving_context(&self) -> Self::SaplingProvingContext { - SaplingProvingContext::new() - } +impl SpendProver for StaticTxProver { + type Proof = Proof; - fn spend_proof( - &self, - ctx: &mut Self::SaplingProvingContext, + fn prepare_circuit( proof_generation_key: ProofGenerationKey, diversifier: Diversifier, rseed: Rseed, - ar: jubjub::Fr, - value: u64, + value: NoteValue, + alpha: jubjub::Fr, + rcv: ValueCommitTrapdoor, anchor: bls12_381::Scalar, - merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - ValueCommitment, - redjubjub::PublicKey, - ), - (), - > { - let (proof, cv, rk) = ctx.spend_proof( + merkle_path: MerklePath, + ) -> Option { + SpendParameters::prepare_circuit( proof_generation_key, diversifier, rseed, - ar, value, + alpha, + rcv, anchor, merkle_path, - unsafe { SAPLING_SPEND_PARAMS.as_ref() } - .expect("Parameters not loaded: SAPLING_SPEND_PARAMS should have been initialized"), - &prepare_verifying_key( - unsafe { SAPLING_SPEND_VK.as_ref() } - .expect("Parameters not loaded: SAPLING_SPEND_VK should have been initialized"), - ), - )?; + ) + } - let mut zkproof = [0u8; GROTH_PROOF_SIZE]; - proof - .write(&mut zkproof[..]) - .expect("should be able to serialize a proof"); + fn create_proof(&self, circuit: circuit::Spend, rng: &mut R) -> Self::Proof { + unsafe { SAPLING_SPEND_PARAMS.as_ref() } + .expect("Parameters not loaded: SAPLING_SPEND_PARAMS should have been initialized") + .create_proof(circuit, rng) + } - Ok((zkproof, cv, rk)) + fn encode_proof(proof: Self::Proof) -> sapling::bundle::GrothProofBytes { + SpendParameters::encode_proof(proof) } +} - fn output_proof( - &self, - ctx: &mut Self::SaplingProvingContext, +impl OutputProver for StaticTxProver { + type Proof = Proof; + + fn prepare_circuit( esk: jubjub::Fr, payment_address: PaymentAddress, rcm: jubjub::Fr, - value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], ValueCommitment) { - let (proof, cv) = ctx.output_proof( - esk, - payment_address, - rcm, - value, - unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.expect( - "Parameters not loaded: SAPLING_OUTPUT_PARAMS should have been initialized", - ), - ); - - let mut zkproof = [0u8; GROTH_PROOF_SIZE]; - proof - .write(&mut zkproof[..]) - .expect("should be able to serialize a proof"); + value: NoteValue, + rcv: ValueCommitTrapdoor, + ) -> circuit::Output { + OutputParameters::prepare_circuit(esk, payment_address, rcm, value, rcv) + } - (zkproof, cv) + fn create_proof(&self, circuit: circuit::Output, rng: &mut R) -> Self::Proof { + unsafe { SAPLING_OUTPUT_PARAMS.as_ref() } + .expect("Parameters not loaded: SAPLING_OUTPUT_PARAMS should have been initialized") + .create_proof(circuit, rng) } - fn binding_sig( - &self, - ctx: &mut Self::SaplingProvingContext, - value_balance: zcash_primitives::transaction::components::Amount, - sighash: &[u8; 32], - ) -> Result { - ctx.binding_sig(value_balance, sighash) + fn encode_proof(proof: Self::Proof) -> sapling::bundle::GrothProofBytes { + OutputParameters::encode_proof(proof) } } -pub(crate) struct SaplingBuilder(sapling::builder::SaplingBuilder); +pub(crate) struct SaplingBuilder { + builder: sapling::builder::Builder, + signing_keys: Vec, +} -pub(crate) fn new_sapling_builder(network: &Network, target_height: u32) -> Box { - Box::new(SaplingBuilder(sapling::builder::SaplingBuilder::new( - *network, - target_height.into(), - ))) +pub(crate) fn new_sapling_builder( + network: &Network, + target_height: u32, + anchor: [u8; 32], + coinbase: bool, +) -> Result, String> { + let bundle_type = if coinbase { + BundleType::Coinbase + } else { + BundleType::DEFAULT + }; + + let anchor = Option::from(Anchor::from_bytes(anchor)) + .ok_or_else(|| "Invalid Sapling anchor".to_owned())?; + + Ok(Box::new(SaplingBuilder { + builder: sapling::builder::Builder::new( + sapling_zip212_enforcement(network, target_height.into()), + bundle_type, + anchor, + ), + signing_keys: vec![], + })) } #[allow(clippy::boxed_local)] pub(crate) fn build_sapling_bundle( builder: Box, - target_height: u32, ) -> Result, String> { - builder.build(target_height).map(Box::new) + builder.build().map(Box::new) } impl SaplingBuilder { pub(crate) fn add_spend( &mut self, extsk: &[u8], - diversifier: [u8; 11], recipient: [u8; 43], value: u64, rcm: [u8; 32], @@ -468,7 +413,6 @@ impl SaplingBuilder { ) -> Result<(), String> { let extsk = ExtendedSpendingKey::from_bytes(extsk).map_err(|_| "Invalid ExtSK".to_owned())?; - let diversifier = Diversifier(diversifier); let recipient = PaymentAddress::from_bytes(&recipient).ok_or("Invalid recipient address")?; let value = NoteValue::from_raw(value); @@ -481,9 +425,12 @@ impl SaplingBuilder { let merkle_path = merkle_path_from_slice(&merkle_path) .map_err(|e| format!("Invalid Sapling Merkle path: {}", e))?; - self.0 - .add_spend(OsRng, extsk, diversifier, note, merkle_path) - .map_err(|e| format!("Failed to add Sapling spend: {}", e)) + self.builder + .add_spend(&extsk, note, merkle_path) + .map_err(|e| format!("Failed to add Sapling spend: {}", e))?; + self.signing_keys.push(extsk.expsk.ask); + + Ok(()) } pub(crate) fn add_recipient( @@ -496,28 +443,39 @@ impl SaplingBuilder { let ovk = Some(OutgoingViewingKey(ovk)); let to = PaymentAddress::from_bytes(&to).ok_or("Invalid recipient address")?; let value = NoteValue::from_raw(value); - let memo = MemoBytes::from_bytes(&memo).map_err(|e| format!("Invalid memo: {}", e))?; + let _ = MemoBytes::from_bytes(&memo).map_err(|e| format!("Invalid memo: {}", e))?; - self.0 - .add_output(OsRng, ovk, to, value, memo) + self.builder + .add_output(ovk, to, value, Some(memo)) .map_err(|e| format!("Failed to add Sapling recipient: {}", e)) } - fn build(self, target_height: u32) -> Result { + fn build(self) -> Result { + let Self { + builder, + signing_keys, + } = self; let prover = crate::sapling::StaticTxProver; - let mut ctx = prover.new_sapling_proving_context(); let rng = OsRng; - let bundle = self - .0 - .build(&prover, &mut ctx, rng, target_height.into(), None) - .map_err(|e| format!("Failed to build Sapling bundle: {}", e))?; - Ok(SaplingUnauthorizedBundle { bundle, ctx }) + let bundle = builder + .build::(rng) + .map_err(|e| format!("Failed to build Sapling bundle: {}", e))? + .map(|(bundle, _)| bundle.create_proofs(&prover, &prover, rng, ())); + Ok(SaplingUnauthorizedBundle { + bundle, + signing_keys, + }) } } pub(crate) struct SaplingUnauthorizedBundle { - pub(crate) bundle: Option>, - ctx: SaplingProvingContext, + pub(crate) bundle: Option< + sapling::Bundle< + sapling::builder::InProgress, + Amount, + >, + >, + signing_keys: Vec, } pub(crate) fn apply_sapling_bundle_signatures( @@ -529,12 +487,14 @@ pub(crate) fn apply_sapling_bundle_signatures( impl SaplingUnauthorizedBundle { fn apply_signatures(self, sighash_bytes: [u8; 32]) -> Result { - let SaplingUnauthorizedBundle { bundle, mut ctx } = self; + let SaplingUnauthorizedBundle { + bundle, + signing_keys, + } = self; let authorized = if let Some(bundle) = bundle { - let prover = crate::sapling::StaticTxProver; - let (authorized, _) = bundle - .apply_signatures(&prover, &mut ctx, &mut OsRng, &sighash_bytes) + let authorized = bundle + .apply_signatures(OsRng, sighash_bytes, &signing_keys) .map_err(|e| format!("Failed to apply signatures to Sapling bundle: {}", e))?; Some(authorized) } else { @@ -548,10 +508,7 @@ impl SaplingUnauthorizedBundle { pub(crate) struct Verifier(SaplingVerificationContext); pub(crate) fn init_verifier() -> Box { - // We consider ZIP 216 active all of the time because blocks prior to NU5 - // activation (on mainnet and testnet) did not contain Sapling transactions - // that violated its canonicity rule. - Box::new(Verifier(SaplingVerificationContext::new(true))) + Box::new(Verifier(SaplingVerificationContext::new())) } impl Verifier { @@ -580,16 +537,13 @@ impl Verifier { }; // Deserialize rk - let rk = match redjubjub::PublicKey::read(&rk[..]) { + let rk = match redjubjub::VerificationKey::try_from(*rk) { Ok(p) => p, Err(_) => return false, }; // Deserialize the signature - let spend_auth_sig = match Signature::read(&spend_auth_sig[..]) { - Ok(sig) => sig, - Err(_) => return false, - }; + let spend_auth_sig = redjubjub::Signature::from(*spend_auth_sig); // Deserialize the proof let zkproof = match Proof::read(&zkproof[..]) { @@ -605,10 +559,9 @@ impl Verifier { sighash_value, spend_auth_sig, zkproof, - &prepare_verifying_key( - unsafe { SAPLING_SPEND_VK.as_ref() } - .expect("Parameters not loaded: SAPLING_SPEND_VK should have been initialized"), - ), + &unsafe { SAPLING_SPEND_VK.as_ref() } + .expect("Parameters not loaded: SAPLING_SPEND_VK should have been initialized") + .prepare(), ) } @@ -648,11 +601,9 @@ impl Verifier { cmu, epk, zkproof, - &prepare_verifying_key( - unsafe { SAPLING_OUTPUT_VK.as_ref() }.expect( - "Parameters not loaded: SAPLING_OUTPUT_VK should have been initialized", - ), - ), + &unsafe { SAPLING_OUTPUT_VK.as_ref() } + .expect("Parameters not loaded: SAPLING_OUTPUT_VK should have been initialized") + .prepare(), ) } @@ -668,10 +619,7 @@ impl Verifier { }; // Deserialize the signature - let binding_sig = match Signature::read(&binding_sig[..]) { - Ok(sig) => sig, - Err(_) => return false, - }; + let binding_sig = redjubjub::Signature::from(*binding_sig); self.0 .final_check(value_balance, sighash_value, binding_sig) @@ -679,7 +627,7 @@ impl Verifier { } struct BatchValidatorInner { - validator: sapling_proofs::BatchValidator, + validator: sapling::BatchValidator, queued_entries: CacheEntries, } @@ -687,7 +635,7 @@ pub(crate) struct BatchValidator(Option); pub(crate) fn init_batch_validator(cache_store: bool) -> Box { Box::new(BatchValidator(Some(BatchValidatorInner { - validator: sapling_proofs::BatchValidator::new(), + validator: sapling::BatchValidator::new(), queued_entries: CacheEntries::new(cache_store), }))) } diff --git a/src/rust/src/sapling/spec.rs b/src/rust/src/sapling/spec.rs index 87aefb8deb..3ed3a830a8 100644 --- a/src/rust/src/sapling/spec.rs +++ b/src/rust/src/sapling/spec.rs @@ -5,16 +5,14 @@ use group::{cofactor::CofactorGroup, GroupEncoding}; use incrementalmerkletree::Hashable; use rand_core::{OsRng, RngCore}; -use zcash_primitives::{ +use sapling::{ constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, - merkle_tree::HashSer, - sapling::{ - merkle_hash, - note::{ExtractedNoteCommitment, NoteCommitment}, - value::NoteValue, - Diversifier, Node, Note, NullifierDerivingKey, PaymentAddress, Rseed, - }, + merkle_hash, + note::{ExtractedNoteCommitment, NoteCommitment}, + value::NoteValue, + Diversifier, Node, Note, NullifierDerivingKey, PaymentAddress, Rseed, }; +use zcash_primitives::merkle_tree::HashSer; use crate::de_ct; diff --git a/src/rust/src/sapling/zip32.rs b/src/rust/src/sapling/zip32.rs index 3bdb170457..54e3e2c374 100644 --- a/src/rust/src/sapling/zip32.rs +++ b/src/rust/src/sapling/zip32.rs @@ -1,7 +1,9 @@ -use zcash_primitives::{ - sapling::{keys::FullViewingKey, Diversifier}, - zip32::{self, sapling_address, sapling_derive_internal_fvk, sapling_find_address}, +use sapling::{ + keys::FullViewingKey, + zip32::{sapling_address, sapling_derive_internal_fvk, sapling_find_address}, + Diversifier, }; +use zcash_primitives::zip32; #[cxx::bridge] mod ffi { @@ -20,7 +22,6 @@ mod ffi { fn xsk_master(seed: &[u8]) -> [u8; 169]; fn xsk_derive(xsk_parent: &[u8; 169], i: u32) -> [u8; 169]; fn xsk_derive_internal(xsk_external: &[u8; 169]) -> [u8; 169]; - fn xfvk_derive(xfvk_parent: &[u8; 169], i: u32) -> Result<[u8; 169]>; fn derive_internal_fvk(fvk: &[u8; 96], dk: [u8; 32]) -> FfiFullViewingKey; fn address(fvk: &[u8; 96], dk: [u8; 32], j: [u8; 11]) -> Result<[u8; 43]>; fn find_address(fvk: &[u8; 96], dk: [u8; 32], j: [u8; 11]) -> Result; @@ -30,7 +31,7 @@ mod ffi { /// Derives the master ExtendedSpendingKey from a seed. fn xsk_master(seed: &[u8]) -> [u8; 169] { - let xsk = zip32::ExtendedSpendingKey::master(seed); + let xsk = sapling::zip32::ExtendedSpendingKey::master(seed); let mut xsk_master = [0; 169]; xsk.write(&mut xsk_master[..]) @@ -40,9 +41,9 @@ fn xsk_master(seed: &[u8]) -> [u8; 169] { /// Derive a child ExtendedSpendingKey from a parent. fn xsk_derive(xsk_parent: &[u8; 169], i: u32) -> [u8; 169] { - let xsk_parent = - zip32::ExtendedSpendingKey::read(&xsk_parent[..]).expect("valid ExtendedSpendingKey"); - let i = zip32::ChildIndex::from_index(i); + let xsk_parent = sapling::zip32::ExtendedSpendingKey::read(&xsk_parent[..]) + .expect("valid ExtendedSpendingKey"); + let i = zip32::ChildIndex::from_index(i).expect("non-hardened derivation is unsupported"); let xsk = xsk_parent.derive_child(i); @@ -55,8 +56,8 @@ fn xsk_derive(xsk_parent: &[u8; 169], i: u32) -> [u8; 169] { /// Derive the Sapling internal spending key from the external extended /// spending key fn xsk_derive_internal(xsk_external: &[u8; 169]) -> [u8; 169] { - let xsk_external = - zip32::ExtendedSpendingKey::read(&xsk_external[..]).expect("valid ExtendedSpendingKey"); + let xsk_external = sapling::zip32::ExtendedSpendingKey::read(&xsk_external[..]) + .expect("valid ExtendedSpendingKey"); let xsk_internal = xsk_external.derive_internal(); @@ -67,26 +68,10 @@ fn xsk_derive_internal(xsk_external: &[u8; 169]) -> [u8; 169] { xsk_internal_ret } -/// Derive a child ExtendedFullViewingKey from a parent. -fn xfvk_derive(xfvk_parent: &[u8; 169], i: u32) -> Result<[u8; 169], String> { - let xfvk_parent = zip32::ExtendedFullViewingKey::read(&xfvk_parent[..]) - .expect("valid ExtendedFullViewingKey"); - let i = zip32::ChildIndex::from_index(i); - - let xfvk = xfvk_parent - .derive_child(i) - .map_err(|()| "Cannot derive hardened xfvk".to_string())?; - - let mut xfvk_i = [0; 169]; - xfvk.write(&mut xfvk_i[..]) - .expect("should be able to serialize an ExtendedFullViewingKey"); - Ok(xfvk_i) -} - /// Derive the Sapling internal full viewing key from the corresponding external full viewing key fn derive_internal_fvk(fvk: &[u8; 96], dk: [u8; 32]) -> ffi::FfiFullViewingKey { let fvk = FullViewingKey::read(&fvk[..]).expect("valid Sapling FullViewingKey"); - let dk = zip32::sapling::DiversifierKey::from_bytes(dk); + let dk = sapling::zip32::DiversifierKey::from_bytes(dk); let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&fvk, &dk); @@ -99,8 +84,8 @@ fn derive_internal_fvk(fvk: &[u8; 96], dk: [u8; 32]) -> ffi::FfiFullViewingKey { /// Derive a PaymentAddress from an ExtendedFullViewingKey. fn address(fvk: &[u8; 96], dk: [u8; 32], j: [u8; 11]) -> Result<[u8; 43], String> { let fvk = FullViewingKey::read(&fvk[..]).expect("valid Sapling FullViewingKey"); - let dk = zip32::sapling::DiversifierKey::from_bytes(dk); - let j = zip32::DiversifierIndex(j); + let dk = sapling::zip32::DiversifierKey::from_bytes(dk); + let j = zip32::DiversifierIndex::from(j); sapling_address(&fvk, &dk, j) .ok_or_else(|| "Diversifier index does not produce a valid diversifier".to_string()) @@ -114,21 +99,21 @@ fn find_address( j: [u8; 11], ) -> Result { let fvk = FullViewingKey::read(&fvk[..]).expect("valid Sapling FullViewingKey"); - let dk = zip32::sapling::DiversifierKey::from_bytes(dk); - let j = zip32::DiversifierIndex(j); + let dk = sapling::zip32::DiversifierKey::from_bytes(dk); + let j = zip32::DiversifierIndex::from(j); sapling_find_address(&fvk, &dk, j) .ok_or_else(|| "No valid diversifiers at or above given index".to_string()) .map(|(j, addr)| ffi::FfiPaymentAddress { - j: j.0, + j: *j.as_bytes(), addr: addr.to_bytes(), }) } fn diversifier_index(dk: [u8; 32], d: [u8; 11]) -> [u8; 11] { - let dk = zip32::sapling::DiversifierKey::from_bytes(dk); + let dk = sapling::zip32::DiversifierKey::from_bytes(dk); let diversifier = Diversifier(d); let j = dk.diversifier_index(&diversifier); - j.0 + *j.as_bytes() } diff --git a/src/rust/src/test_harness_ffi.rs b/src/rust/src/test_harness_ffi.rs index 2d22d741e2..078ec4cf84 100644 --- a/src/rust/src/test_harness_ffi.rs +++ b/src/rust/src/test_harness_ffi.rs @@ -1,10 +1,10 @@ +use std::convert::TryFrom; + use group::{ff::Field, Group, GroupEncoding}; use rand::{thread_rng, Rng}; +use sapling::value::ValueCommitment; use zcash_note_encryption::EphemeralKeyBytes; -use zcash_primitives::{ - sapling::{self, redjubjub, value::ValueCommitment}, - transaction::components::{sapling as sapling_tx, Amount}, -}; +use zcash_primitives::transaction::components::Amount; pub(crate) fn test_only_invalid_sapling_bundle( spends: usize, @@ -27,14 +27,17 @@ pub(crate) fn test_only_invalid_sapling_bundle( .unwrap(); let anchor = jubjub::Base::random(&mut rng); let nullifier = sapling::Nullifier(rng.gen()); - let rk = redjubjub::PublicKey(jubjub::ExtendedPoint::random(&mut rng)); + let rk = redjubjub::VerificationKey::try_from( + jubjub::ExtendedPoint::random(&mut rng).to_bytes(), + ) + .unwrap(); let zkproof = gen_array(&mut rng); let spend_auth_sig = { let tmp = gen_array::<_, 64>(&mut rng); - redjubjub::Signature::read(&tmp[..]).unwrap() + redjubjub::Signature::from(tmp) }; - sapling_tx::SpendDescription::temporary_zcashd_from_parts( + sapling::bundle::SpendDescription::from_parts( cv, anchor, nullifier, @@ -61,7 +64,7 @@ pub(crate) fn test_only_invalid_sapling_bundle( let out_ciphertext = gen_array(&mut rng); let zkproof = gen_array(&mut rng); - sapling_tx::OutputDescription::temporary_zcashd_from_parts( + sapling::bundle::OutputDescription::from_parts( cv, cmu, ephemeral_key, @@ -74,16 +77,16 @@ pub(crate) fn test_only_invalid_sapling_bundle( let binding_sig = { let tmp = gen_array::<_, 64>(&mut rng); - redjubjub::Signature::read(&tmp[..]).unwrap() + redjubjub::Signature::from(tmp) }; - let bundle = sapling_tx::Bundle::temporary_zcashd_from_parts( + let bundle = sapling::Bundle::from_parts( spends, outputs, Amount::from_i64(value_balance).unwrap(), - sapling_tx::Authorized { binding_sig }, + sapling::bundle::Authorized { binding_sig }, ); - Box::new(crate::sapling::Bundle(Some(bundle))) + Box::new(crate::sapling::Bundle(bundle)) } pub(crate) fn test_only_replace_sapling_nullifier( @@ -92,18 +95,18 @@ pub(crate) fn test_only_replace_sapling_nullifier( nullifier: [u8; 32], ) { if let Some(bundle) = bundle.0.as_mut() { - *bundle = sapling_tx::Bundle::temporary_zcashd_from_parts( + *bundle = sapling::Bundle::from_parts( bundle .shielded_spends() .iter() .enumerate() .map(|(i, spend)| { if i == spend_index { - sapling_tx::SpendDescription::temporary_zcashd_from_parts( + sapling::bundle::SpendDescription::from_parts( spend.cv().clone(), *spend.anchor(), sapling::Nullifier(nullifier), - spend.rk().clone(), + *spend.rk(), *spend.zkproof(), *spend.spend_auth_sig(), ) @@ -116,6 +119,7 @@ pub(crate) fn test_only_replace_sapling_nullifier( *bundle.value_balance(), *bundle.authorization(), ) + .expect("Prior bundle was valid"); } } @@ -127,7 +131,7 @@ pub(crate) fn test_only_replace_sapling_output_parts( out_ciphertext: [u8; 80], ) { if let Some(bundle) = bundle.0.as_mut() { - *bundle = sapling_tx::Bundle::temporary_zcashd_from_parts( + *bundle = sapling::Bundle::from_parts( bundle.shielded_spends().to_vec(), bundle .shielded_outputs() @@ -135,7 +139,7 @@ pub(crate) fn test_only_replace_sapling_output_parts( .enumerate() .map(|(i, output)| { if i == output_index { - sapling_tx::OutputDescription::temporary_zcashd_from_parts( + sapling::bundle::OutputDescription::from_parts( output.cv().clone(), sapling::note::ExtractedNoteCommitment::from_bytes(&cmu).unwrap(), output.ephemeral_key().clone(), @@ -151,5 +155,6 @@ pub(crate) fn test_only_replace_sapling_output_parts( *bundle.value_balance(), *bundle.authorization(), ) + .expect("Prior bundle was valid"); } } diff --git a/src/rust/src/tests/key_components.rs b/src/rust/src/tests/key_components.rs index f268bb1d91..8cc8d8f7b8 100644 --- a/src/rust/src/tests/key_components.rs +++ b/src/rust/src/tests/key_components.rs @@ -1,7 +1,7 @@ use group::GroupEncoding; -use zcash_primitives::{ - constants::SPENDING_KEY_GENERATOR, - sapling::{Diversifier, Nullifier, ProofGenerationKey, Rseed}, +use sapling::{ + constants::SPENDING_KEY_GENERATOR, keys::SpendValidatingKey, value::NoteValue, Diversifier, + Nullifier, ProofGenerationKey, Rseed, }; use crate::sapling::spec::{ask_to_ak, check_diversifier, crh_ivk, ivk_to_pkd, nsk_to_nk}; @@ -661,7 +661,10 @@ fn key_components() { assert_eq!(&ak, &tv.ak); } - let pgk = ProofGenerationKey { ak, nsk }; + let pgk = ProofGenerationKey { + ak: SpendValidatingKey::temporary_zcash_from_bytes(&ak.to_bytes()).unwrap(), + nsk, + }; let fvk = pgk.to_viewing_key(); assert_eq!(&fvk.nk.0.to_bytes(), &tv.nk); { @@ -686,7 +689,7 @@ fn key_components() { } let note_r = jubjub::Scalar::from_bytes(&tv.note_r).unwrap(); - let note = addr.create_note(tv.note_v, Rseed::BeforeZip212(note_r)); + let note = addr.create_note(NoteValue::from_raw(tv.note_v), Rseed::BeforeZip212(note_r)); assert_eq!(¬e.cmu().to_bytes(), &tv.note_cm); assert_eq!(note.nf(&fvk.nk, tv.note_pos), Nullifier(tv.note_nf)); diff --git a/src/rust/src/tests/mod.rs b/src/rust/src/tests/mod.rs index 942f02082d..b09a085659 100644 --- a/src/rust/src/tests/mod.rs +++ b/src/rust/src/tests/mod.rs @@ -1,5 +1,5 @@ use group::GroupEncoding; -use zcash_primitives::constants::{ +use sapling::constants::{ NOTE_COMMITMENT_RANDOMNESS_GENERATOR, NULLIFIER_POSITION_GENERATOR, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR, VALUE_COMMITMENT_VALUE_GENERATOR, diff --git a/src/rust/src/tests/signatures.rs b/src/rust/src/tests/signatures.rs index 028e3a0161..31d01ab90c 100644 --- a/src/rust/src/tests/signatures.rs +++ b/src/rust/src/tests/signatures.rs @@ -1,7 +1,4 @@ -use zcash_primitives::{ - constants::SPENDING_KEY_GENERATOR, - sapling::redjubjub::{PrivateKey, PublicKey, Signature}, -}; +use std::convert::TryFrom; #[test] fn redjubjub_signatures() { @@ -16,7 +13,7 @@ fn redjubjub_signatures() { rsig: [u8; 64], } - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_signatures.py + // From https://github.com/zcash/zcash-test-vectors/blob/master/zcash_test_vectors/sapling/redjubjub.py let test_vectors = vec![ TestVector { sk: [ @@ -50,18 +47,18 @@ fn redjubjub_signatures() { 0x00, 0x00, 0x00, 0x00, ], sig: [ - 0xea, 0xa0, 0x57, 0x47, 0x6b, 0x4a, 0xb4, 0x82, 0x28, 0x8b, 0x93, 0xdf, 0x8f, 0xe0, - 0xc5, 0xce, 0x9d, 0x78, 0x83, 0x67, 0xf2, 0xbe, 0x55, 0x1b, 0x7f, 0x7a, 0x82, 0xa6, - 0xdb, 0x36, 0x04, 0x68, 0xde, 0xb9, 0xa7, 0xb7, 0xaf, 0xaa, 0xdf, 0xec, 0xa6, 0xf4, - 0x81, 0x19, 0x3d, 0xc6, 0x57, 0x57, 0x47, 0xf6, 0x0a, 0x1a, 0x8a, 0x48, 0xff, 0x0a, - 0xd7, 0x0c, 0xf8, 0xcb, 0x8d, 0x52, 0x8e, 0x08, + 0xdc, 0xa3, 0xbb, 0x2c, 0xb8, 0xf0, 0x48, 0xcc, 0xab, 0x10, 0xae, 0xd7, 0x75, 0x46, + 0xc1, 0xdb, 0xb1, 0x0c, 0xc4, 0xfb, 0x15, 0xab, 0x02, 0xac, 0xae, 0xf9, 0x44, 0xdd, + 0xab, 0x8b, 0x67, 0x22, 0x54, 0x5f, 0xda, 0x4c, 0x62, 0x04, 0x6d, 0x69, 0xd9, 0x8f, + 0x92, 0x2f, 0x4e, 0x8c, 0x21, 0x0b, 0xc4, 0x7b, 0x4f, 0xdd, 0xe0, 0xa1, 0x94, 0x71, + 0x79, 0x80, 0x4c, 0x1a, 0xce, 0x56, 0x90, 0x05, ], rsig: [ - 0xd5, 0x6f, 0x0d, 0x91, 0xaf, 0x42, 0x4e, 0x1f, 0x1c, 0x7f, 0xb8, 0x6b, 0xa4, 0xee, - 0xd1, 0x43, 0xcc, 0x16, 0x66, 0x0c, 0x5f, 0xe8, 0xd7, 0xdc, 0x0d, 0x28, 0x4b, 0xcf, - 0x65, 0xa0, 0x89, 0xe9, 0x8b, 0x56, 0x1f, 0x9f, 0x20, 0x1a, 0x63, 0x3d, 0x70, 0x0c, - 0xd3, 0x98, 0x1e, 0x8c, 0xac, 0x07, 0xb5, 0xa8, 0x7e, 0xfa, 0x61, 0x86, 0x06, 0x2d, - 0xd8, 0xe5, 0xd6, 0x32, 0x5e, 0x7b, 0x82, 0x02, + 0x70, 0xc2, 0x84, 0x50, 0x4e, 0x90, 0xf0, 0x00, 0x8e, 0x8e, 0xd2, 0x20, 0x8f, 0x49, + 0x69, 0x72, 0x7a, 0x41, 0x5e, 0xc3, 0x10, 0x2c, 0x29, 0x9e, 0x39, 0x8b, 0x6c, 0x16, + 0x57, 0x2b, 0xd9, 0x64, 0x3e, 0xe1, 0x01, 0x17, 0x66, 0x68, 0x1e, 0x40, 0x6e, 0xe6, + 0xbe, 0xe3, 0xd0, 0x3e, 0xe8, 0xf2, 0x71, 0x76, 0xe3, 0x2f, 0xba, 0xbd, 0xde, 0xd2, + 0x0b, 0x0d, 0x17, 0x86, 0xa4, 0xee, 0x18, 0x01, ], }, TestVector { @@ -96,18 +93,18 @@ fn redjubjub_signatures() { 0x01, 0x01, 0x01, 0x01, ], sig: [ - 0x22, 0x35, 0x54, 0x94, 0xa8, 0x31, 0x6a, 0xb1, 0x34, 0x73, 0xf5, 0x5e, 0x62, 0x66, - 0xb2, 0xfb, 0x41, 0x97, 0x31, 0x5e, 0xac, 0x62, 0xf8, 0x2c, 0xc7, 0x3d, 0xca, 0xca, - 0x19, 0x90, 0x90, 0xf1, 0x5b, 0xe1, 0x98, 0xce, 0x7d, 0x3f, 0x9f, 0xc8, 0xff, 0xf5, - 0x50, 0xe1, 0x08, 0x81, 0xec, 0x49, 0xff, 0x27, 0x36, 0x9e, 0x7d, 0x4f, 0xd9, 0x64, - 0x01, 0x53, 0x49, 0x2a, 0x0a, 0x06, 0x25, 0x08, + 0xb5, 0xa1, 0xf3, 0x2d, 0x3d, 0x50, 0xfc, 0x73, 0x8b, 0x5c, 0x3b, 0x4e, 0x99, 0x60, + 0x72, 0x9c, 0xe4, 0x31, 0x6b, 0xa7, 0x72, 0x1a, 0x12, 0x68, 0x66, 0x04, 0xfe, 0xba, + 0x6b, 0xd7, 0x48, 0x45, 0x00, 0x70, 0xcb, 0x92, 0x24, 0x06, 0xfd, 0xfc, 0x5d, 0x60, + 0xde, 0xa9, 0xbe, 0x3a, 0x52, 0x6a, 0x16, 0xcf, 0xeb, 0x87, 0x77, 0x79, 0xfb, 0x78, + 0x2d, 0x5d, 0x41, 0x39, 0x5b, 0x45, 0x5f, 0x04, ], rsig: [ - 0xf4, 0xb8, 0x94, 0xba, 0x84, 0xce, 0x1e, 0xc3, 0x8a, 0x63, 0x15, 0x2f, 0xc4, 0x09, - 0xf9, 0x47, 0xd6, 0x1a, 0xbb, 0x1f, 0x48, 0x91, 0x63, 0x6b, 0xc3, 0xee, 0x19, 0xef, - 0x6d, 0x4b, 0x30, 0xc0, 0xfd, 0x22, 0x86, 0x6b, 0x84, 0xff, 0xbc, 0x7e, 0x2a, 0x78, - 0xc4, 0x3f, 0x57, 0x83, 0xd2, 0xd2, 0xea, 0xd0, 0x78, 0x59, 0x55, 0x03, 0x74, 0x43, - 0xc2, 0xf4, 0xd5, 0x2f, 0x78, 0x5e, 0xee, 0x07, + 0x5a, 0x5a, 0x20, 0xd2, 0x00, 0xef, 0xdd, 0xd4, 0x98, 0xdf, 0xae, 0x2a, 0x9e, 0xf8, + 0xcf, 0x01, 0x28, 0x1a, 0x89, 0x19, 0x01, 0x8a, 0x82, 0x4c, 0xc7, 0xa4, 0x98, 0x3b, + 0x9a, 0x0d, 0x4a, 0x06, 0xff, 0x17, 0x20, 0x79, 0xe0, 0x13, 0xd4, 0x2a, 0x2a, 0x3a, + 0x88, 0xa6, 0x52, 0x0c, 0x86, 0xfc, 0xe3, 0xb9, 0x8e, 0x1e, 0xfa, 0xa3, 0x25, 0x83, + 0x2a, 0x6a, 0x56, 0x58, 0xd8, 0xdd, 0x7c, 0x0a, ], }, TestVector { @@ -142,18 +139,18 @@ fn redjubjub_signatures() { 0x02, 0x02, 0x02, 0x02, ], sig: [ - 0xdd, 0x65, 0x21, 0x01, 0x4d, 0xff, 0x70, 0x6e, 0x3a, 0x38, 0x52, 0x7a, 0x86, 0xb6, - 0xc1, 0x6e, 0x94, 0x14, 0x80, 0xe7, 0x33, 0xef, 0xf7, 0x9e, 0xbe, 0x0c, 0x43, 0x03, - 0x79, 0xd7, 0x57, 0x04, 0x9d, 0xb7, 0x90, 0xcd, 0x5e, 0x14, 0x44, 0x7c, 0x38, 0x6f, - 0x5f, 0xcb, 0x41, 0x9f, 0x27, 0xc4, 0x41, 0x3f, 0x35, 0x88, 0xfa, 0x21, 0x42, 0xd2, - 0xcf, 0xba, 0xed, 0x08, 0x2c, 0xc6, 0xdb, 0x07, + 0x1f, 0x3e, 0x8a, 0x94, 0x31, 0x0c, 0x20, 0x71, 0xa7, 0x0f, 0x9d, 0xf5, 0xe7, 0x9a, + 0xa9, 0xe8, 0x48, 0x5d, 0xec, 0xcb, 0x17, 0x8b, 0xdf, 0xf9, 0x80, 0x5f, 0xcb, 0xe6, + 0xf7, 0xd5, 0x51, 0xee, 0xe3, 0xc3, 0x54, 0x2c, 0xa7, 0x5c, 0x9d, 0x8d, 0x4a, 0xdc, + 0x54, 0xd7, 0x2c, 0x3d, 0xbe, 0x28, 0x62, 0x6d, 0x20, 0x78, 0x5b, 0xb7, 0xf5, 0x88, + 0xc1, 0xa5, 0x82, 0xb8, 0x93, 0xdb, 0xb6, 0x01, ], rsig: [ - 0xd8, 0x94, 0x45, 0xcb, 0x9b, 0xd1, 0x03, 0x35, 0x69, 0x23, 0x1d, 0xd6, 0x28, 0xaa, - 0x62, 0x81, 0x09, 0xfe, 0x93, 0x50, 0x2b, 0xf2, 0x2f, 0x9a, 0x5f, 0x37, 0xb1, 0x4e, - 0x51, 0x7f, 0x9a, 0x20, 0x54, 0xae, 0xe3, 0xc8, 0x1b, 0x60, 0xb3, 0xf0, 0x55, 0x1e, - 0x32, 0xf7, 0x93, 0x5a, 0xbc, 0x2f, 0x37, 0xb9, 0x9a, 0xb3, 0xec, 0x99, 0x68, 0x02, - 0xef, 0xd6, 0x50, 0x69, 0xe1, 0x28, 0x12, 0x08, + 0xd1, 0x36, 0x21, 0x4c, 0x5d, 0x52, 0x8e, 0xa3, 0xd4, 0xcb, 0x7b, 0x63, 0x1a, 0x6b, + 0xb0, 0x36, 0x06, 0x49, 0x73, 0xa1, 0x08, 0xb7, 0x33, 0xa5, 0xe3, 0xa4, 0x52, 0xab, + 0x52, 0xa6, 0x59, 0xe5, 0x67, 0xcb, 0x55, 0xd2, 0x64, 0x4e, 0x74, 0xb6, 0xe8, 0x42, + 0x6f, 0x2a, 0x7d, 0xd2, 0xa0, 0x4d, 0x2d, 0xda, 0x49, 0x35, 0xcc, 0x38, 0x20, 0xb7, + 0x7a, 0x9c, 0x1a, 0xb6, 0x19, 0x86, 0x3c, 0x05, ], }, TestVector { @@ -188,18 +185,18 @@ fn redjubjub_signatures() { 0x03, 0x03, 0x03, 0x03, ], sig: [ - 0x72, 0x79, 0xa7, 0x5c, 0x01, 0x36, 0x75, 0xb3, 0x29, 0x84, 0xe5, 0xc7, 0x3a, 0x98, - 0x91, 0xeb, 0xf0, 0xb2, 0x29, 0xb1, 0x6e, 0x62, 0x35, 0xba, 0x36, 0xdf, 0xa1, 0xb5, - 0xa1, 0x0c, 0x5e, 0x44, 0x57, 0x81, 0x91, 0x89, 0x7c, 0x06, 0xb8, 0x52, 0x4a, 0x26, - 0x74, 0xaa, 0x7a, 0x0c, 0x8c, 0x23, 0x5f, 0x52, 0xd3, 0x3a, 0xc9, 0x2c, 0x70, 0x56, - 0xb2, 0xbe, 0x95, 0x3c, 0x3f, 0xaa, 0x3d, 0x07, + 0x12, 0xc7, 0x8d, 0xdd, 0x20, 0xd3, 0x0a, 0x61, 0xf8, 0x93, 0x0c, 0x6f, 0xe0, 0x85, + 0x0f, 0xd1, 0x12, 0xbb, 0x7b, 0xe8, 0x8b, 0x12, 0x38, 0xea, 0x33, 0xd6, 0xbe, 0xf8, + 0x81, 0xc1, 0x02, 0xd1, 0x04, 0xaa, 0x36, 0x54, 0x4a, 0x78, 0x47, 0x1c, 0x9e, 0x28, + 0x42, 0xe6, 0xfd, 0x42, 0x55, 0x83, 0x46, 0xcf, 0xf4, 0x31, 0x27, 0x03, 0x26, 0x66, + 0xeb, 0x11, 0x6f, 0x44, 0x2a, 0x28, 0x48, 0x0c, ], rsig: [ - 0xaa, 0xd4, 0x82, 0x8c, 0xb3, 0x42, 0xcf, 0x09, 0xb0, 0x0e, 0x30, 0x2c, 0xbb, 0xe7, - 0xcc, 0x3e, 0x95, 0xfe, 0x1f, 0xf8, 0x28, 0x74, 0x8e, 0x5f, 0x5b, 0xc6, 0x9c, 0xbf, - 0xde, 0x6e, 0x27, 0x22, 0xd7, 0x64, 0x35, 0x68, 0x7e, 0x85, 0x0c, 0xd3, 0x07, 0xa9, - 0xc1, 0x82, 0xec, 0x10, 0xe6, 0x88, 0x1d, 0xd6, 0x5e, 0xed, 0xc1, 0x1f, 0xa7, 0xb4, - 0x6d, 0xe3, 0xa7, 0x19, 0x59, 0xce, 0xc0, 0x02, + 0x01, 0xba, 0xaa, 0x26, 0x27, 0x4c, 0x14, 0x9a, 0xcf, 0x12, 0xe1, 0xcc, 0xf5, 0x50, + 0x7d, 0x56, 0x79, 0x04, 0x82, 0xf0, 0x67, 0xe5, 0xc9, 0x2b, 0x32, 0x19, 0xad, 0x6b, + 0xf9, 0x11, 0x18, 0xcc, 0x3f, 0xce, 0x8d, 0x2a, 0x23, 0x19, 0x8a, 0x3b, 0x29, 0x0a, + 0x7b, 0xf6, 0x8c, 0x2a, 0xc0, 0x7b, 0x5d, 0x90, 0x62, 0xb9, 0xf8, 0x68, 0x66, 0x2b, + 0xb2, 0x52, 0x49, 0x12, 0xd4, 0x85, 0x6e, 0x0c, ], }, TestVector { @@ -234,18 +231,18 @@ fn redjubjub_signatures() { 0x04, 0x04, 0x04, 0x04, ], sig: [ - 0x51, 0x23, 0xb3, 0x1f, 0x84, 0xaf, 0x0c, 0x35, 0x5e, 0x13, 0xe7, 0x8a, 0x64, 0xd7, - 0xa3, 0xcd, 0xfd, 0x6b, 0xdf, 0xfd, 0xc7, 0x33, 0x38, 0xd9, 0x31, 0x7f, 0x73, 0x43, - 0x91, 0xa5, 0x5a, 0xe6, 0x25, 0x8f, 0x69, 0x80, 0xb9, 0xc7, 0xd1, 0x90, 0xcf, 0xa3, - 0x65, 0x81, 0xa9, 0xa4, 0x7a, 0x86, 0x3f, 0xd3, 0xbf, 0x76, 0x59, 0x42, 0x22, 0x95, - 0xb7, 0x5f, 0xd1, 0x22, 0xc3, 0xdd, 0x8a, 0x05, + 0x77, 0x4a, 0xc4, 0x67, 0x3f, 0x09, 0xf3, 0xac, 0x57, 0x89, 0xb2, 0x86, 0xb5, 0xee, + 0xcb, 0xed, 0xb2, 0x57, 0x23, 0x4e, 0x8c, 0xdf, 0xd9, 0x3f, 0x02, 0x89, 0x09, 0x78, + 0xa6, 0xbb, 0xa6, 0x11, 0x69, 0xed, 0x48, 0xf9, 0xe1, 0xc9, 0xfd, 0x13, 0x19, 0xbd, + 0x33, 0x0d, 0x2c, 0xf5, 0xb4, 0x91, 0x01, 0x0d, 0x69, 0xb0, 0x43, 0xf4, 0x64, 0x8b, + 0xff, 0x55, 0x41, 0x62, 0xc6, 0xa6, 0xdc, 0x09, ], rsig: [ - 0x5b, 0xae, 0x25, 0x4f, 0xbd, 0xed, 0x60, 0x7a, 0x5c, 0x48, 0xb5, 0x30, 0x29, 0xf5, - 0x9b, 0xa7, 0x06, 0x32, 0x48, 0x79, 0xaa, 0x18, 0xd9, 0xc4, 0x73, 0x19, 0x00, 0x4b, - 0xe0, 0x2c, 0xec, 0xe0, 0xb8, 0xbb, 0x02, 0x4a, 0x7a, 0xab, 0xaa, 0x0a, 0x64, 0x0f, - 0x3a, 0x54, 0xdc, 0xda, 0xf2, 0x11, 0x31, 0x46, 0x9a, 0x50, 0x06, 0xbe, 0x27, 0x81, - 0xa5, 0x67, 0xff, 0xa6, 0x50, 0x3a, 0x35, 0x03, + 0x7c, 0x6c, 0x49, 0x8d, 0xe0, 0x01, 0x78, 0x61, 0x09, 0xb3, 0x03, 0xa4, 0xc5, 0xdc, + 0xb7, 0xfd, 0x07, 0x57, 0x50, 0xa0, 0xb9, 0xdf, 0x5e, 0x1e, 0x2a, 0x8e, 0x75, 0x47, + 0xb7, 0xed, 0x70, 0xcc, 0x0b, 0x56, 0xa5, 0xbf, 0xa9, 0x65, 0x78, 0x43, 0xef, 0xd8, + 0x9c, 0x66, 0xa8, 0x4f, 0x41, 0xd2, 0xb1, 0xb5, 0x07, 0x51, 0x19, 0x6b, 0x1e, 0x8c, + 0x0c, 0x44, 0x98, 0x60, 0x06, 0x96, 0xa4, 0x04, ], }, TestVector { @@ -280,18 +277,18 @@ fn redjubjub_signatures() { 0x05, 0x05, 0x05, 0x05, ], sig: [ - 0xdc, 0x18, 0xc8, 0x8d, 0x96, 0x44, 0x42, 0x40, 0x6d, 0x65, 0x0a, 0xa2, 0xff, 0xbd, - 0x83, 0xd1, 0x13, 0xbf, 0x6a, 0x19, 0xda, 0x78, 0xf2, 0x66, 0x5b, 0x29, 0x4f, 0xa5, - 0xfa, 0x45, 0x0b, 0x92, 0x81, 0xa0, 0x7e, 0x32, 0x0c, 0x1a, 0xa3, 0x1d, 0x32, 0x44, - 0x9e, 0x00, 0xc5, 0xc3, 0x2d, 0xb2, 0xf4, 0x13, 0xdf, 0x0b, 0x63, 0xd0, 0x72, 0x8f, - 0xa4, 0x09, 0x41, 0xa8, 0xda, 0x02, 0x4f, 0x01, + 0x9a, 0x25, 0x42, 0x9f, 0x3e, 0xfd, 0x9b, 0x2f, 0x7d, 0xe2, 0x9e, 0x45, 0x12, 0x8d, + 0xd7, 0xb7, 0x60, 0xf0, 0x50, 0x8c, 0xd9, 0x58, 0x21, 0x82, 0xab, 0xaf, 0x53, 0xdd, + 0x76, 0xc0, 0x34, 0x2c, 0xe4, 0x1b, 0x4a, 0xcf, 0x8e, 0x0a, 0x48, 0x24, 0xe4, 0x11, + 0x08, 0xc2, 0x02, 0x65, 0x73, 0x11, 0x4b, 0x60, 0xbe, 0xec, 0xb1, 0x74, 0x01, 0x2a, + 0x2b, 0xdb, 0xee, 0xcb, 0xaa, 0x00, 0xb5, 0x06, ], rsig: [ - 0x59, 0xe2, 0xe8, 0x18, 0x76, 0x6c, 0x50, 0xfc, 0x8f, 0x38, 0x40, 0xb2, 0x72, 0xaf, - 0x9a, 0xd9, 0x47, 0x56, 0xc8, 0x41, 0x32, 0x95, 0xfc, 0x79, 0x5f, 0xaf, 0xbc, 0xc0, - 0x71, 0x8e, 0x6c, 0x08, 0x16, 0x9a, 0x00, 0xd5, 0x83, 0x02, 0x77, 0x2a, 0x28, 0x28, - 0x43, 0xe8, 0x88, 0xd9, 0x81, 0xfa, 0x04, 0x79, 0x5d, 0x01, 0x4c, 0xf9, 0xc8, 0xcd, - 0xb9, 0x07, 0xff, 0x1b, 0x43, 0x0d, 0x92, 0x00, + 0xcf, 0xf5, 0x83, 0x57, 0x13, 0xbe, 0x07, 0xfb, 0xe1, 0x25, 0xbb, 0xf2, 0x7a, 0x63, + 0x6a, 0xdd, 0x13, 0x1c, 0x90, 0x81, 0x71, 0x6c, 0x52, 0xfd, 0xa8, 0x75, 0x42, 0x6d, + 0x03, 0x98, 0x2c, 0xd2, 0x7e, 0xbd, 0x14, 0xb4, 0x22, 0x7b, 0x83, 0x96, 0x15, 0xfd, + 0x03, 0x71, 0xbf, 0xdb, 0x8a, 0x30, 0xab, 0xdd, 0xff, 0x74, 0xd7, 0x95, 0xf3, 0xe2, + 0x7d, 0x1d, 0x47, 0xc6, 0x29, 0x46, 0x9b, 0x08, ], }, TestVector { @@ -326,18 +323,18 @@ fn redjubjub_signatures() { 0x06, 0x06, 0x06, 0x06, ], sig: [ - 0x9a, 0xf6, 0xf2, 0x80, 0x0f, 0x4b, 0x80, 0xf7, 0x93, 0xbe, 0x64, 0x8a, 0x43, 0x9f, - 0x86, 0xe5, 0x7d, 0xa1, 0xb9, 0x19, 0x99, 0x9e, 0x41, 0x91, 0x09, 0x99, 0xd4, 0x2e, - 0xd0, 0xf3, 0x89, 0x6d, 0xb7, 0x6e, 0x06, 0x38, 0x8b, 0x27, 0x2c, 0x99, 0x85, 0x8b, - 0x55, 0x04, 0xd0, 0x2e, 0xc6, 0xb4, 0xd5, 0x25, 0xb8, 0x71, 0x38, 0x10, 0x50, 0x5f, - 0x4f, 0xc0, 0x31, 0x08, 0x3a, 0x14, 0xbf, 0x09, + 0xbb, 0xe0, 0x23, 0x59, 0x87, 0xc6, 0xe0, 0xec, 0x68, 0x6d, 0xdb, 0x8a, 0x65, 0x72, + 0x66, 0xad, 0x60, 0x5f, 0x7b, 0x75, 0x95, 0x5b, 0xb0, 0xe8, 0x02, 0xf8, 0x81, 0x64, + 0xa0, 0xff, 0xe1, 0x0c, 0x3b, 0x73, 0x85, 0x04, 0xab, 0xb3, 0xd1, 0x05, 0x62, 0xb9, + 0x27, 0xb3, 0xd2, 0x9f, 0xe9, 0xb0, 0xd3, 0x56, 0x28, 0x6a, 0xea, 0xe5, 0xa2, 0xac, + 0x9e, 0x43, 0x5f, 0x20, 0x79, 0x1a, 0xf8, 0x00, ], rsig: [ - 0x3f, 0x7d, 0x50, 0x71, 0xb8, 0x76, 0x17, 0x49, 0x05, 0x71, 0xa8, 0xbe, 0x91, 0x74, - 0x9e, 0x69, 0xf6, 0xbc, 0xba, 0x5a, 0xb6, 0x26, 0xe4, 0x2f, 0xf9, 0x2d, 0x0d, 0x7d, - 0xab, 0x73, 0xf3, 0x03, 0x61, 0xe5, 0xa2, 0x24, 0x99, 0x8e, 0x1f, 0x5e, 0xa1, 0xe5, - 0xf8, 0x68, 0x9a, 0x06, 0xa2, 0x77, 0x48, 0xbf, 0x74, 0x19, 0x63, 0xef, 0x51, 0x33, - 0x22, 0xf4, 0xa1, 0xba, 0x99, 0xaa, 0x36, 0x03, + 0x6d, 0xe3, 0x2b, 0x54, 0x15, 0xd7, 0x7a, 0x90, 0x5f, 0x09, 0x03, 0x90, 0x2a, 0x11, + 0x7e, 0xda, 0x79, 0x3c, 0x70, 0x8e, 0x23, 0xa5, 0x42, 0x45, 0xba, 0x8a, 0x8d, 0x1f, + 0xe0, 0x26, 0x75, 0x23, 0x23, 0x15, 0x65, 0xe0, 0x57, 0x09, 0xae, 0xd9, 0x6c, 0x22, + 0x1f, 0xb1, 0xf3, 0xd0, 0x42, 0x04, 0x35, 0x03, 0xff, 0x33, 0x85, 0x85, 0xa9, 0xbb, + 0x98, 0x9c, 0x9d, 0xd4, 0x30, 0xd6, 0xd6, 0x0b, ], }, TestVector { @@ -372,18 +369,18 @@ fn redjubjub_signatures() { 0x07, 0x07, 0x07, 0x07, ], sig: [ - 0x64, 0x59, 0x67, 0x6a, 0x94, 0x16, 0x34, 0xec, 0xb6, 0x1e, 0x59, 0xb7, 0x9a, 0x98, - 0xab, 0xe5, 0x87, 0x6f, 0x35, 0x6f, 0x72, 0x8a, 0xa0, 0x9e, 0x0c, 0xca, 0x9e, 0xfe, - 0x05, 0x76, 0x1a, 0x33, 0x09, 0xaa, 0x88, 0xb2, 0xfa, 0x0e, 0xe2, 0xd0, 0x4c, 0x1c, - 0x46, 0xe9, 0xf2, 0xa0, 0x48, 0xd5, 0x9d, 0x55, 0x65, 0xaf, 0xa6, 0xc3, 0xf1, 0x5b, - 0xce, 0x70, 0x8d, 0xaa, 0xab, 0x7b, 0x34, 0x0e, + 0x44, 0x6d, 0x67, 0x7c, 0x4c, 0xfe, 0xfd, 0x02, 0x4b, 0x0a, 0xeb, 0x37, 0xa5, 0x98, + 0xcc, 0x2e, 0xb3, 0xd2, 0x9b, 0x02, 0x94, 0xfe, 0x5b, 0xb6, 0x97, 0x8e, 0x8b, 0x43, + 0xd3, 0x2b, 0x2e, 0x4f, 0x09, 0x56, 0xac, 0xd1, 0x3e, 0x7e, 0x3a, 0x63, 0xa1, 0x8f, + 0xca, 0x32, 0xd6, 0xab, 0x94, 0xb9, 0x4e, 0xd0, 0x33, 0xe9, 0xa1, 0x0f, 0xc5, 0x69, + 0x28, 0xbc, 0x8a, 0x0f, 0x4f, 0x8e, 0x95, 0x00, ], rsig: [ - 0xc9, 0x66, 0x84, 0xec, 0x7e, 0xa6, 0x0b, 0xde, 0x87, 0x88, 0x22, 0xdd, 0xca, 0xf6, - 0xb8, 0xb0, 0xbd, 0x31, 0x98, 0x51, 0x54, 0xdf, 0x9a, 0xd4, 0xf6, 0x90, 0x7d, 0xf8, - 0xfe, 0xd9, 0x5c, 0x1d, 0x84, 0xfe, 0x67, 0xe6, 0x78, 0x75, 0xa5, 0x39, 0x55, 0x0e, - 0xb2, 0x51, 0x4f, 0x19, 0x3b, 0x8e, 0xd4, 0x57, 0x25, 0x6c, 0x8d, 0x30, 0x28, 0x1d, - 0x6f, 0x8b, 0xb9, 0x54, 0x49, 0x24, 0xca, 0x0c, + 0x8d, 0xe0, 0x41, 0xe7, 0x09, 0xdb, 0x62, 0x4a, 0xe2, 0xbe, 0x16, 0x48, 0xb6, 0x62, + 0x23, 0x9c, 0xde, 0xdf, 0x85, 0xec, 0xd3, 0x82, 0x26, 0x8b, 0x0e, 0x35, 0x54, 0xbf, + 0xa0, 0xf2, 0x08, 0x1c, 0xd6, 0x41, 0xbc, 0xa0, 0x40, 0x78, 0xaa, 0x89, 0xf7, 0xdd, + 0x25, 0x40, 0x58, 0x7c, 0xed, 0x6b, 0x45, 0x89, 0x16, 0xb1, 0x3e, 0x4b, 0x6a, 0x36, + 0x30, 0xda, 0x69, 0x76, 0x46, 0xdb, 0xbf, 0x09, ], }, TestVector { @@ -418,18 +415,18 @@ fn redjubjub_signatures() { 0x08, 0x08, 0x08, 0x08, ], sig: [ - 0x24, 0x93, 0x2c, 0x1f, 0xaa, 0x01, 0x63, 0xca, 0x9a, 0x7f, 0xcd, 0xe4, 0x76, 0x11, - 0x29, 0xd2, 0xe5, 0xe9, 0x9c, 0xf5, 0xef, 0xa2, 0x5d, 0x27, 0x04, 0x58, 0x8e, 0x1c, - 0x75, 0x67, 0x7b, 0x5e, 0xeb, 0xe4, 0x55, 0x04, 0x8d, 0x7c, 0xe1, 0xb0, 0xd2, 0x01, - 0x27, 0x53, 0xf7, 0x1b, 0x27, 0x25, 0x01, 0x2e, 0xe1, 0x85, 0x49, 0x28, 0x73, 0x18, - 0xf9, 0xcd, 0x73, 0xf0, 0x7f, 0x0f, 0xb5, 0x02, + 0x99, 0x35, 0x80, 0xef, 0x93, 0x34, 0x9a, 0x1c, 0x9e, 0xe9, 0x60, 0xca, 0x3e, 0x7c, + 0xd0, 0x4c, 0x13, 0xb4, 0xa0, 0xec, 0x4f, 0xd1, 0x80, 0x53, 0xa1, 0x9c, 0xff, 0x77, + 0x63, 0x62, 0x09, 0x65, 0xfb, 0xee, 0x96, 0xc1, 0x64, 0x72, 0x30, 0xe3, 0x73, 0xcb, + 0x82, 0xb8, 0x1d, 0x00, 0x03, 0x92, 0x23, 0xd3, 0x0b, 0x39, 0x3e, 0xd1, 0x72, 0xc9, + 0xb3, 0xc5, 0x63, 0xc6, 0x11, 0x79, 0x22, 0x05, ], rsig: [ - 0xf7, 0xfa, 0x26, 0xca, 0x22, 0xf3, 0x86, 0xc4, 0x3c, 0x19, 0x1a, 0x0b, 0x3e, 0xa6, - 0x57, 0x7e, 0x8e, 0xea, 0xa3, 0xf3, 0x6b, 0x9b, 0xd1, 0xa3, 0xac, 0x3d, 0xf6, 0xf8, - 0x83, 0xa3, 0xff, 0xdb, 0x31, 0x32, 0x0b, 0xde, 0x62, 0x7f, 0xf4, 0x6f, 0xc2, 0x26, - 0x4a, 0x32, 0x63, 0xb9, 0xab, 0x67, 0x12, 0x3b, 0xa5, 0xe1, 0x08, 0x43, 0x20, 0xd9, - 0x10, 0xb3, 0x94, 0xef, 0x8c, 0x65, 0xba, 0x09, + 0xcc, 0x7a, 0xae, 0x1c, 0xed, 0xad, 0x2d, 0x7f, 0x6c, 0xe0, 0x4c, 0x19, 0xc5, 0xa5, + 0xb6, 0xb7, 0xa6, 0xa0, 0x82, 0x78, 0x5c, 0x54, 0x0c, 0x14, 0xf6, 0x30, 0x9b, 0x06, + 0x4d, 0x1f, 0xfa, 0x68, 0x17, 0x29, 0x53, 0xfb, 0xa0, 0xc2, 0xfc, 0xfb, 0x87, 0x5c, + 0xa7, 0xf7, 0xea, 0x98, 0xef, 0x55, 0xa0, 0x40, 0x2f, 0xd5, 0x29, 0xcf, 0xcd, 0xdf, + 0x99, 0x6c, 0xa2, 0xb8, 0xca, 0x89, 0x90, 0x0a, ], }, TestVector { @@ -464,47 +461,37 @@ fn redjubjub_signatures() { 0x09, 0x09, 0x09, 0x09, ], sig: [ - 0x64, 0xab, 0xd1, 0x25, 0xbf, 0xc4, 0xc6, 0x54, 0xfa, 0xf2, 0xb6, 0xdd, 0x75, 0x3e, - 0xc6, 0x90, 0x22, 0x4d, 0xbc, 0xab, 0x8c, 0xd6, 0x32, 0xdd, 0x59, 0x3c, 0x91, 0xce, - 0x3a, 0xb0, 0xbc, 0xad, 0xca, 0x92, 0x76, 0x34, 0x02, 0x1c, 0x31, 0x47, 0x6c, 0x78, - 0xc5, 0xac, 0x7c, 0xcc, 0xab, 0xbd, 0x6f, 0x92, 0x7d, 0xf2, 0x05, 0xea, 0xa7, 0x07, - 0xcc, 0x00, 0xd4, 0x7d, 0x39, 0xf3, 0xe4, 0x0c, + 0xce, 0x90, 0xdd, 0xf4, 0xaf, 0x21, 0xaa, 0xc4, 0xd9, 0x41, 0x93, 0xea, 0x16, 0xff, + 0x35, 0xcd, 0x93, 0x79, 0x20, 0x4e, 0x7d, 0x8f, 0xf4, 0xc0, 0xf5, 0x41, 0x17, 0xab, + 0xb1, 0x6b, 0x7c, 0x85, 0xa0, 0xb1, 0x97, 0xcf, 0x13, 0xab, 0x14, 0xd7, 0xc3, 0xba, + 0x68, 0x01, 0x0a, 0xb8, 0x05, 0x12, 0x25, 0x91, 0x3b, 0xdb, 0xc3, 0x9a, 0x51, 0xf6, + 0x03, 0x7a, 0xfc, 0x6c, 0xee, 0xcb, 0x0b, 0x06, ], rsig: [ - 0xeb, 0x7a, 0x06, 0x5d, 0x75, 0xf8, 0x45, 0xdc, 0x09, 0x41, 0xb7, 0x09, 0xc0, 0xb1, - 0x49, 0xea, 0xfd, 0x80, 0x5e, 0xa5, 0x8f, 0x38, 0x0b, 0x92, 0xb9, 0xd3, 0x10, 0x8a, - 0x56, 0x1b, 0xda, 0x17, 0x85, 0xdf, 0x8f, 0x10, 0x1e, 0x0e, 0x14, 0x0f, 0xca, 0xee, - 0x99, 0xb7, 0xdb, 0xb7, 0xdf, 0xbf, 0x7e, 0x61, 0xf3, 0xa1, 0x2f, 0x46, 0x09, 0x50, - 0x69, 0xe0, 0x6e, 0x88, 0x96, 0xa9, 0xe4, 0x04, + 0xa8, 0x47, 0x74, 0x2e, 0x94, 0x01, 0xcf, 0x22, 0x39, 0x21, 0x3d, 0xc8, 0x81, 0x3e, + 0x97, 0x72, 0xe9, 0x7a, 0xf8, 0xd6, 0x7a, 0xdf, 0xfe, 0xab, 0xc8, 0xe6, 0x7f, 0x5d, + 0x2d, 0x90, 0xd0, 0xb4, 0x1b, 0xc2, 0x5b, 0x05, 0xf9, 0x4a, 0xce, 0x16, 0x8a, 0xec, + 0xc6, 0x58, 0x3e, 0x18, 0xf7, 0x63, 0x74, 0x92, 0xf3, 0x7a, 0x9c, 0xa3, 0x00, 0x20, + 0x2b, 0xc0, 0x65, 0xab, 0xd3, 0x80, 0xec, 0x00, ], }, ]; for tv in test_vectors { - let sk = PrivateKey::read(&tv.sk[..]).unwrap(); - let vk = PublicKey::read(&tv.vk[..]).unwrap(); - let rvk = PublicKey::read(&tv.rvk[..]).unwrap(); - let sig = Signature::read(&tv.sig[..]).unwrap(); - let rsig = Signature::read(&tv.rsig[..]).unwrap(); + let sk = redjubjub::SigningKey::try_from(tv.sk).unwrap(); + let vk = redjubjub::VerificationKey::try_from(tv.vk).unwrap(); + let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap(); + let sig = redjubjub::Signature::from(tv.sig); + let rsig = redjubjub::Signature::from(tv.rsig); let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap(); - { - let mut vec = Vec::new(); - sk.randomize(alpha).write(&mut vec).unwrap(); - assert_eq!(&vec, &tv.rsk); - } - { - let mut vec = Vec::new(); - vk.randomize(alpha, SPENDING_KEY_GENERATOR) - .write(&mut vec) - .unwrap(); - assert_eq!(&vec, &tv.rvk); - } + assert_eq!(<[u8; 32]>::from(sk.randomize(&alpha)), tv.rsk); + assert_eq!(<[u8; 32]>::from(vk.randomize(&alpha)), tv.rvk); - assert!(vk.verify(&tv.m, &sig, SPENDING_KEY_GENERATOR)); - assert!(rvk.verify(&tv.m, &rsig, SPENDING_KEY_GENERATOR)); - assert!(!vk.verify(&tv.m, &rsig, SPENDING_KEY_GENERATOR)); - assert!(!rvk.verify(&tv.m, &sig, SPENDING_KEY_GENERATOR)); + assert_eq!(vk.verify(&tv.m, &sig), Ok(())); + assert_eq!(rvk.verify(&tv.m, &rsig), Ok(())); + assert!(matches!(vk.verify(&tv.m, &rsig), Err(_))); + assert!(matches!(rvk.verify(&tv.m, &sig), Err(_))); } } diff --git a/src/rust/src/transaction_ffi.rs b/src/rust/src/transaction_ffi.rs index e10a5e34e8..95c4ce8dcd 100644 --- a/src/rust/src/transaction_ffi.rs +++ b/src/rust/src/transaction_ffi.rs @@ -6,11 +6,12 @@ use blake2b_simd::Hash; use libc::{c_uchar, size_t}; use tracing::error; use zcash_encoding::Vector; +use zcash_primitives::transaction::components::amount::NonNegativeAmount; use zcash_primitives::{ consensus::BranchId, legacy::Script, transaction::{ - components::{sapling, transparent, Amount}, + components::transparent, sighash::{SignableInput, TransparentAuthorizingContext}, sighash_v5::v5_signature_hash, txid::TxIdDigester, @@ -69,7 +70,7 @@ impl transparent::Authorization for TransparentAuth { } impl TransparentAuthorizingContext for TransparentAuth { - fn input_amounts(&self) -> Vec { + fn input_amounts(&self) -> Vec { self.all_prev_outputs .iter() .map(|prevout| prevout.value) @@ -148,7 +149,7 @@ pub(crate) struct PrecomputedAuth; impl Authorization for PrecomputedAuth { type TransparentAuth = TransparentAuth; - type SaplingAuth = sapling::Authorized; + type SaplingAuth = sapling::bundle::Authorized; type OrchardAuth = orchard::bundle::Authorized; } diff --git a/src/rust/src/unified_keys_ffi.rs b/src/rust/src/unified_keys_ffi.rs index a676f956e7..c416ecbe60 100644 --- a/src/rust/src/unified_keys_ffi.rs +++ b/src/rust/src/unified_keys_ffi.rs @@ -50,9 +50,7 @@ pub extern "C" fn unified_full_viewing_key_parse( Fvk::Sapling(data) => { // The last 32 bytes is the diversifier key, which is opaque. // The remaining 96 bytes should be a valid Sapling FVK. - if zcash_primitives::sapling::keys::FullViewingKey::read(&data[..96]) - .is_err() - { + if sapling::keys::FullViewingKey::read(&data[..96]).is_err() { error!("Unified FVK contains invalid Sapling FVK"); return std::ptr::null_mut(); } diff --git a/src/rust/src/wallet.rs b/src/rust/src/wallet.rs index 1e800e3446..5253ab9c32 100644 --- a/src/rust/src/wallet.rs +++ b/src/rust/src/wallet.rs @@ -13,7 +13,6 @@ use zcash_encoding::{Optional, Vector}; use zcash_primitives::{ consensus::BlockHeight, merkle_tree::{read_position, write_position}, - sapling::NOTE_COMMITMENT_TREE_DEPTH, transaction::{components::Amount, TxId}, }; @@ -150,7 +149,8 @@ pub struct Wallet { nullifiers: BTreeMap, /// The incremental Merkle tree used to track note commitments and witnesses for notes /// belonging to the wallet. - commitment_tree: BridgeTree, + // TODO: Replace this with an `orchard` crate constant (they happen to be the same). + commitment_tree: BridgeTree, /// The block height at which the last checkpoint was created, if any. last_checkpoint: Option, /// The block height and transaction index of the note most recently added to @@ -1370,7 +1370,10 @@ pub extern "C" fn orchard_wallet_load_note_commitment_tree( #[no_mangle] pub extern "C" fn orchard_wallet_init_from_frontier( wallet: *mut Wallet, - frontier: *const bridgetree::Frontier, + frontier: *const bridgetree::Frontier< + MerkleHashOrchard, + { sapling::NOTE_COMMITMENT_TREE_DEPTH }, + >, ) -> bool { let wallet = unsafe { wallet.as_mut() }.expect("Wallet pointer may not be null."); let frontier = unsafe { frontier.as_ref() }.expect("Wallet pointer may not be null."); diff --git a/src/rust/src/wallet_scanner.rs b/src/rust/src/wallet_scanner.rs index 8e4fc9f886..aa6da98ccc 100644 --- a/src/rust/src/wallet_scanner.rs +++ b/src/rust/src/wallet_scanner.rs @@ -10,15 +10,12 @@ use std::sync::{ use crossbeam_channel as channel; use memuse::DynamicUsage; +use sapling::{bundle::GrothProofBytes, note_encryption::SaplingDomain}; use zcash_note_encryption::{batch, BatchDomain, Domain, ShieldedOutput, ENC_CIPHERTEXT_SIZE}; use zcash_primitives::{ block::BlockHash, consensus, - sapling::note_encryption::SaplingDomain, - transaction::{ - components::{sapling::GrothProofBytes, OutputDescription}, - Transaction, TxId, - }, + transaction::{components::OutputDescription, Transaction, TxId}, }; use crate::{bridge::ffi, note_encryption::parse_and_prepare_sapling_ivk, params::Network}; @@ -38,7 +35,7 @@ trait OutputDomain: BatchDomain { const KIND: &'static str; } -impl OutputDomain for SaplingDomain

{ +impl OutputDomain for SaplingDomain { const KIND: &'static str = "sapling"; } @@ -550,7 +547,7 @@ where } type SaplingRunner = - BatchRunner<[u8; 32], SaplingDomain, OutputDescription, WithUsage>; + BatchRunner<[u8; 32], SaplingDomain, OutputDescription, WithUsage>; /// A batch scanner for the `zcashd` wallet. pub(crate) struct BatchScanner { @@ -622,7 +619,7 @@ impl BatchScanner { runner.add_outputs( block_tag, txid, - || SaplingDomain::for_height(params, height), + || SaplingDomain::new(consensus::sapling_zip212_enforcement(¶ms, height)), bundle.shielded_outputs(), ); } @@ -671,7 +668,7 @@ impl BatchScanner { } pub(crate) struct BatchResult { - sapling: HashMap<(TxId, usize), DecryptedNote<[u8; 32], SaplingDomain>>, + sapling: HashMap<(TxId, usize), DecryptedNote<[u8; 32], SaplingDomain>>, } impl BatchResult { diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 595ed58289..ff19cfd059 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -306,9 +306,10 @@ class TxWithNullifiers // The Orchard bundle builder always pads to two Actions, so we can just // use an empty builder to create a dummy Orchard bundle. + // TODO: With the new BundleType::DEFAULT this is no longer true. Fix this. uint256 orchardAnchor; uint256 dataToBeSigned; - auto builder = orchard::Builder(true, true, orchardAnchor); + auto builder = orchard::Builder(false, orchardAnchor); mutableTx.orchardBundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value(); orchardNullifier = mutableTx.orchardBundle.GetNullifiers()[0]; @@ -340,7 +341,7 @@ template<> void AppendRandomLeaf(OrchardMerkleFrontier &tree) { // append a random leaf to OrchardMerkleFrontier. uint256 orchardAnchor; uint256 dataToBeSigned; - auto builder = orchard::Builder(true, true, orchardAnchor); + auto builder = orchard::Builder(false, orchardAnchor); auto bundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value(); tree.AppendBundle(bundle); } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 4f41433716..eaad542d1c 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(zs_address_test) KeyIO keyIO(Params()); for (uint32_t i = 0; i < 1000; i++) { - auto sk = m.Derive(i); + auto sk = m.Derive(i | HARDENED_KEY_LIMIT); { std::string sk_string = keyIO.EncodeSpendingKey(sk); BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0); diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index 93bc15764e..a93facf855 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -48,11 +48,10 @@ uint256 ProduceShieldedSignatureHash( namespace orchard { Builder::Builder( - bool spendsEnabled, - bool outputsEnabled, + bool coinbase, uint256 anchor) : inner(nullptr, orchard_builder_free) { - inner.reset(orchard_builder_new(spendsEnabled, outputsEnabled, anchor.IsNull() ? nullptr : anchor.begin())); + inner.reset(orchard_builder_new(coinbase, anchor.IsNull() ? nullptr : anchor.begin())); } bool Builder::AddSpend(orchard::SpendInfo spendInfo) @@ -209,6 +208,7 @@ TransactionBuilder::TransactionBuilder( const CChainParams& params, int nHeight, std::optional orchardAnchor, + uint256 saplingAnchor, const CKeyStore* keystore, const CCoinsViewCache* coinsView, CCriticalSection* cs_coinsView) : @@ -218,7 +218,8 @@ TransactionBuilder::TransactionBuilder( coinsView(coinsView), cs_coinsView(cs_coinsView), orchardAnchor(orchardAnchor), - saplingBuilder(sapling::new_builder(*params.RustNetwork(), nHeight)) + saplingAnchor(saplingAnchor), + saplingBuilder(sapling::new_builder(*params.RustNetwork(), nHeight, saplingAnchor.ToRawBytes(), false)) { mtx = CreateNewContextualCMutableTransaction( consensusParams, nHeight, @@ -226,7 +227,7 @@ TransactionBuilder::TransactionBuilder( // Ignore the Orchard anchor if we can't use it yet. if (orchardAnchor.has_value() && mtx.nVersion >= ZIP225_MIN_TX_VERSION) { - orchardBuilder = orchard::Builder(true, true, orchardAnchor.value()); + orchardBuilder = orchard::Builder(false, orchardAnchor.value()); } } @@ -329,7 +330,6 @@ void TransactionBuilder::AddSaplingSpend( saplingBuilder->add_spend( {reinterpret_cast(ssExtSk.data()), ssExtSk.size()}, - note.d, recipient.GetRawBytes(), note.value(), note.rcm().GetRawBytes(), @@ -525,7 +525,7 @@ TransactionBuilderResult TransactionBuilder::Build() std::optional> maybeSaplingBundle; try { - maybeSaplingBundle = sapling::build_bundle(std::move(saplingBuilder), nHeight); + maybeSaplingBundle = sapling::build_bundle(std::move(saplingBuilder)); } catch (rust::Error e) { return TransactionBuilderResult("Failed to build Sapling bundle: " + std::string(e.what())); } diff --git a/src/transaction_builder.h b/src/transaction_builder.h index 7645ac8772..4d4121ab6a 100644 --- a/src/transaction_builder.h +++ b/src/transaction_builder.h @@ -91,7 +91,7 @@ class Builder { Builder() : inner(nullptr, orchard_builder_free), hasActions(false) { } public: - Builder(bool spendsEnabled, bool outputsEnabled, uint256 anchor); + Builder(bool coinbase, uint256 anchor); // Builder should never be copied Builder(const Builder&) = delete; @@ -242,6 +242,7 @@ class TransactionBuilder std::optional orchardAnchor; std::optional orchardBuilder; CAmount valueBalanceOrchard = 0; + uint256 saplingAnchor; rust::Box saplingBuilder; CAmount valueBalanceSapling = 0; @@ -268,6 +269,7 @@ class TransactionBuilder const CChainParams& params, int nHeight, std::optional orchardAnchor, + uint256 saplingAnchor, const CKeyStore* keyStore = nullptr, const CCoinsViewCache* coinsView = nullptr, CCriticalSection* cs_coinsView = nullptr); @@ -286,6 +288,7 @@ class TransactionBuilder orchardAnchor(std::move(builder.orchardAnchor)), orchardBuilder(std::move(builder.orchardBuilder)), valueBalanceOrchard(std::move(builder.valueBalanceOrchard)), + saplingAnchor(std::move(builder.saplingAnchor)), saplingBuilder(std::move(builder.saplingBuilder)), valueBalanceSapling(std::move(builder.valueBalanceSapling)), orchardSpendingKeys(std::move(orchardSpendingKeys)), @@ -308,8 +311,10 @@ class TransactionBuilder cs_coinsView = std::move(builder.cs_coinsView); mtx = std::move(builder.mtx); fee = std::move(builder.fee); + orchardAnchor = std::move(builder.orchardAnchor); orchardBuilder = std::move(builder.orchardBuilder); valueBalanceOrchard = std::move(builder.valueBalanceOrchard); + saplingAnchor = std::move(builder.saplingAnchor); saplingBuilder = std::move(builder.saplingBuilder); valueBalanceSapling = std::move(builder.valueBalanceSapling); orchardSpendingKeys = std::move(builder.orchardSpendingKeys), diff --git a/src/util/test.cpp b/src/util/test.cpp index 9bb4ee387d..2f74ce8c8a 100644 --- a/src/util/test.cpp +++ b/src/util/test.cpp @@ -352,8 +352,9 @@ CWalletTx GetValidSaplingReceive(const CChainParams& params, // To zaddr auto fvk = sk.expsk.full_viewing_key(); auto pa = sk.ToXFVK().DefaultAddress(); + auto saplingAnchor = SaplingMerkleTree::empty_root(); - auto builder = TransactionBuilder(params, 1, std::nullopt, &keyStore); + auto builder = TransactionBuilder(params, 1, std::nullopt, saplingAnchor, &keyStore); builder.SetFee(0); builder.AddTransparentInput(COutPoint(), scriptPubKey, value); builder.AddSaplingOutput(fvk.ovk, pa, value, {}); diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index a30281db89..19052d8c43 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -16,7 +16,8 @@ const int MIGRATION_EXPIRY_DELTA = 450; -AsyncRPCOperation_saplingmigration::AsyncRPCOperation_saplingmigration(int targetHeight) : targetHeight_(targetHeight) {} +AsyncRPCOperation_saplingmigration::AsyncRPCOperation_saplingmigration(int targetHeight, uint256 saplingAnchor) : + targetHeight_(targetHeight), saplingAnchor_(saplingAnchor) {} AsyncRPCOperation_saplingmigration::~AsyncRPCOperation_saplingmigration() {} @@ -112,7 +113,14 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { CCoinsViewCache coinsView(pcoinsTip); do { CAmount amountToSend = chooseAmount(availableFunds); - auto builder = TransactionBuilder(Params(), targetHeight_, std::nullopt, pwalletMain, &coinsView, &cs_main); + auto builder = TransactionBuilder( + Params(), + targetHeight_, + std::nullopt, + saplingAnchor_, + pwalletMain, + &coinsView, + &cs_main); builder.SetExpiryHeight(targetHeight_ + MIGRATION_EXPIRY_DELTA); LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - LEGACY_DEFAULT_FEE)); std::vector fromNotes; diff --git a/src/wallet/asyncrpcoperation_saplingmigration.h b/src/wallet/asyncrpcoperation_saplingmigration.h index 7d1d085efa..be39eef798 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.h +++ b/src/wallet/asyncrpcoperation_saplingmigration.h @@ -9,7 +9,7 @@ class AsyncRPCOperation_saplingmigration : public AsyncRPCOperation { public: - AsyncRPCOperation_saplingmigration(int targetHeight); + AsyncRPCOperation_saplingmigration(int targetHeight, uint256 saplingAnchor); virtual ~AsyncRPCOperation_saplingmigration(); // We don't want to be copied or moved around @@ -28,6 +28,7 @@ class AsyncRPCOperation_saplingmigration : public AsyncRPCOperation private: int targetHeight_; + uint256 saplingAnchor_; bool main_impl(); diff --git a/src/wallet/gtest/test_orchard_wallet.cpp b/src/wallet/gtest/test_orchard_wallet.cpp index a52d91f3fc..25922427e0 100644 --- a/src/wallet/gtest/test_orchard_wallet.cpp +++ b/src/wallet/gtest/test_orchard_wallet.cpp @@ -32,7 +32,7 @@ CTransaction FakeOrchardTx(const OrchardSpendingKey& sk, libzcash::diversifier_i // Create a shielding transaction from transparent to Orchard // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 fee - auto builder = TransactionBuilder(Params(), 1, orchardAnchor, &keystore); + auto builder = TransactionBuilder(Params(), 1, orchardAnchor, SaplingMerkleTree::empty_root(), &keystore); builder.SetFee(10000); builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 50000); builder.AddOrchardOutput(std::nullopt, recipient, 40000, std::nullopt); @@ -129,7 +129,7 @@ TEST(TransactionBuilder, OrchardToOrchard) { // Create an Orchard-only transaction // 0.0004 z-ZEC in, 0.00025 z-ZEC out, default fee, 0.00014 z-ZEC change - auto builder = TransactionBuilder(Params(), 2, orchardAnchor); + auto builder = TransactionBuilder(Params(), 2, orchardAnchor, SaplingMerkleTree::empty_root()); EXPECT_TRUE(builder.AddOrchardSpend(sk, std::move(spendInfo[0].second))); builder.AddOrchardOutput(std::nullopt, recipient, 25000, std::nullopt); auto maybeTx = builder.Build(); diff --git a/src/wallet/gtest/test_rpc_wallet.cpp b/src/wallet/gtest/test_rpc_wallet.cpp index 749243c0e0..5373788336 100644 --- a/src/wallet/gtest/test_rpc_wallet.cpp +++ b/src/wallet/gtest/test_rpc_wallet.cpp @@ -330,65 +330,77 @@ TEST(WalletRPCTests, RPCZsendmanyTaddrToSapling) ss >> tx; ASSERT_NE(tx.GetSaplingOutputsCount(), 0); - auto outputs = tx.GetSaplingOutputs(); - auto enc_ciphertext = outputs[0].enc_ciphertext(); - auto out_ciphertext = outputs[0].out_ciphertext(); - auto cv = outputs[0].cv(); - auto cmu = outputs[0].cmu(); - auto ephemeral_key = outputs[0].ephemeral_key(); - - // We shouldn't be able to decrypt with the empty ovk - EXPECT_THROW(wallet::try_sapling_output_recovery( - *rustNetwork, - nextBlockHeight, - uint256().GetRawBytes(), - { - cv, - cmu, - ephemeral_key, - enc_ciphertext, - out_ciphertext, - }), rust::Error); - - // We shouldn't be able to decrypt with a random ovk - EXPECT_THROW(wallet::try_sapling_output_recovery( - *rustNetwork, - nextBlockHeight, - random_uint256().GetRawBytes(), - { - cv, - cmu, - ephemeral_key, - enc_ciphertext, - out_ciphertext, - }), rust::Error); - auto accountKey = pwalletMain->GetLegacyAccountKey().ToAccountPubKey(); auto ovks = accountKey.GetOVKsForShielding(); - // We should not be able to decrypt with the internal change OVK for shielding - EXPECT_THROW(wallet::try_sapling_output_recovery( - *rustNetwork, - nextBlockHeight, - ovks.first.GetRawBytes(), - { - cv, - cmu, - ephemeral_key, - enc_ciphertext, - out_ciphertext, - }), rust::Error); - // We should be able to decrypt with the external OVK for shielding - EXPECT_NO_THROW(wallet::try_sapling_output_recovery( - *rustNetwork, - nextBlockHeight, - ovks.second.GetRawBytes(), - { - cv, - cmu, - ephemeral_key, - enc_ciphertext, - out_ciphertext, - })); + + auto extDecryptSucceeded = 0; + auto extDecryptFailed = 0; + for (auto& output: tx.GetSaplingOutputs()) { + auto enc_ciphertext = output.enc_ciphertext(); + auto out_ciphertext = output.out_ciphertext(); + auto cv = output.cv(); + auto cmu = output.cmu(); + auto ephemeral_key = output.ephemeral_key(); + + // We shouldn't be able to decrypt with the empty ovk + EXPECT_THROW(wallet::try_sapling_output_recovery( + *rustNetwork, + nextBlockHeight, + uint256().GetRawBytes(), + { + cv, + cmu, + ephemeral_key, + enc_ciphertext, + out_ciphertext, + }), rust::Error); + + // We shouldn't be able to decrypt with a random ovk + EXPECT_THROW(wallet::try_sapling_output_recovery( + *rustNetwork, + nextBlockHeight, + random_uint256().GetRawBytes(), + { + cv, + cmu, + ephemeral_key, + enc_ciphertext, + out_ciphertext, + }), rust::Error); + + // We should not be able to decrypt with the internal change OVK for shielding + EXPECT_THROW(wallet::try_sapling_output_recovery( + *rustNetwork, + nextBlockHeight, + ovks.first.GetRawBytes(), + { + cv, + cmu, + ephemeral_key, + enc_ciphertext, + out_ciphertext, + }), rust::Error); + + // We should be able to decrypt one of the outputs with the external OVK for shielding. + try { + wallet::try_sapling_output_recovery( + *rustNetwork, + nextBlockHeight, + ovks.second.GetRawBytes(), + { + cv, + cmu, + ephemeral_key, + enc_ciphertext, + out_ciphertext, + }); + extDecryptSucceeded += 1; + } catch (...) { + extDecryptFailed += 1; + } + } + EXPECT_EQ(extDecryptSucceeded, 1); + EXPECT_EQ(extDecryptFailed, 1); // Tear down chainActive.SetTip(NULL); diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 0b4f080d03..9fc8b420e2 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -421,13 +421,14 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) { auto cm = note.cmu().value(); SaplingMerkleTree tree; tree.append(cm); + auto anchor = tree.root(); auto witness = tree.witness(); auto nf = note.nullifier(fvk, witness.position()); ASSERT_TRUE(nf); uint256 nullifier = nf.value(); - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, anchor); builder.AddSaplingSpend(sk, note, witness); builder.AddSaplingOutput(fvk.ovk, pk, 50000, {}); builder.SetFee(0); @@ -564,7 +565,7 @@ TEST(WalletTests, FindMySaplingNotes) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(extfvk.fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -708,10 +709,11 @@ TEST(WalletTests, GetConflictedSaplingNotes) { MerkleFrontiers frontiers; frontiers.sapling.append(cm); + auto anchor = frontiers.sapling.root(); auto witness = frontiers.sapling.witness(); // Generate tx to create output note B - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, anchor); builder.AddSaplingSpend(sk, note, witness); builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 35000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -758,13 +760,13 @@ TEST(WalletTests, GetConflictedSaplingNotes) { auto nullifier2 = maybe_nf.value(); // Create transaction to spend note B - auto builder2 = TransactionBuilder(Params(), 2, std::nullopt); + auto builder2 = TransactionBuilder(Params(), 2, std::nullopt, spend_note_witness.root()); builder2.AddSaplingSpend(sk, note2, spend_note_witness); builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 2000, {}); auto tx2 = builder2.Build().GetTxOrThrow(); // Create conflicting transaction which also spends note B - auto builder3 = TransactionBuilder(Params(), 2, std::nullopt); + auto builder3 = TransactionBuilder(Params(), 2, std::nullopt, spend_note_witness.root()); builder3.AddSaplingSpend(sk, note2, spend_note_witness); builder3.AddSaplingOutput(extfvk.fvk.ovk, pk, 1999, {}); auto tx3 = builder3.Build().GetTxOrThrow(); @@ -822,7 +824,7 @@ TEST(WalletTests, GetConflictedOrchardNotes) { auto scriptPubKey = GetScriptForDestination(tkeyid); // Generate a bundle containing output note A. - auto builder = TransactionBuilder(Params(), 1, orchardAnchor, &keystore); + auto builder = TransactionBuilder(Params(), 1, orchardAnchor, SaplingMerkleTree::empty_root(), &keystore); builder.AddTransparentInput(COutPoint(uint256(), 0), scriptPubKey, 5000); builder.AddOrchardOutput(std::nullopt, recipient, 4000, {}); auto maybeTx = builder.Build(); @@ -876,7 +878,7 @@ TEST(WalletTests, GetConflictedOrchardNotes) { auto recipient2 = ivk.Address(j2); // Generate tx to spend note A - auto builder2 = TransactionBuilder(Params(), 2, orchardTree.root()); + auto builder2 = TransactionBuilder(Params(), 2, orchardTree.root(), SaplingMerkleTree::empty_root()); auto noteToSpend = std::move(wallet.GetOrchardSpendInfo(orchardEntries, 1, orchardTree.root())[0]); builder2.AddOrchardSpend(std::move(noteToSpend.first), std::move(noteToSpend.second)); auto maybeTx2 = builder2.Build(); @@ -890,7 +892,7 @@ TEST(WalletTests, GetConflictedOrchardNotes) { // Generate conflicting tx to spend note A auto noteToSpend2 = std::move(wallet.GetOrchardSpendInfo(orchardEntries, 1, orchardTree.root())[0]); - auto builder3 = TransactionBuilder(Params(), 2, orchardTree.root()); + auto builder3 = TransactionBuilder(Params(), 2, orchardTree.root(), SaplingMerkleTree::empty_root()); builder3.AddOrchardSpend(std::move(noteToSpend2.first), std::move(noteToSpend2.second)); auto maybeTx3 = builder3.Build(); EXPECT_TRUE(maybeTx3.IsTx()); @@ -992,7 +994,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(extfvk.fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -1079,7 +1081,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(extfvk.fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -1214,10 +1216,11 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { auto cm = note.cmu().value(); MerkleFrontiers frontiers; frontiers.sapling.append(cm); + auto anchor = frontiers.sapling.root(); auto witness = frontiers.sapling.witness(); // Generate transaction, which sends funds to note B - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, anchor); builder.AddSaplingSpend(sk, note, witness); builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -1278,7 +1281,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { auto nullifier2 = maybe_nf.value(); // Create transaction to spend note B - auto builder2 = TransactionBuilder(Params(), 2, std::nullopt); + auto builder2 = TransactionBuilder(Params(), 2, std::nullopt, spend_note_witness.root()); builder2.AddSaplingSpend(sk, note2, spend_note_witness); builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 12500, {}); auto tx2 = builder2.Build().GetTxOrThrow(); @@ -2046,13 +2049,13 @@ TEST(WalletTests, UpdatedSaplingNoteData) { auto m = GetTestMasterSaplingSpendingKey(); // Generate dummy Sapling address - auto sk = m.Derive(0); + auto sk = m.Derive(0 | HARDENED_KEY_LIMIT); auto expsk = sk.expsk; auto extfvk = sk.ToXFVK(); auto pa = extfvk.DefaultAddress(); // Generate dummy recipient Sapling address - auto sk2 = m.Derive(1); + auto sk2 = m.Derive(1 | HARDENED_KEY_LIMIT); auto expsk2 = sk2.expsk; auto extfvk2 = sk2.ToXFVK(); auto pa2 = extfvk2.DefaultAddress(); @@ -2060,7 +2063,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(Params(), 1, std::nullopt); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, testNote.tree.root()); builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness()); builder.AddSaplingOutput(extfvk.fvk.ovk, pa2, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -2223,7 +2226,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { // Generate shielding tx from transparent to Sapling // 0.0005 t-ZEC in, 0.0004 z-ZEC out, default fee - auto builder = TransactionBuilder(Params(), 1, std::nullopt, &keystore); + auto builder = TransactionBuilder(Params(), 1, std::nullopt, SaplingMerkleTree::empty_root(), &keystore); builder.AddTransparentInput( COutPoint(uint256S("7777777777777777777777777777777777777777777777777777777777777777"), 0), scriptPubKey, 5000); @@ -2234,7 +2237,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { EXPECT_EQ(tx1.vout.size(), 0); EXPECT_EQ(tx1.vJoinSplit.size(), 0); EXPECT_EQ(tx1.GetSaplingSpendsCount(), 0); - EXPECT_EQ(tx1.GetSaplingOutputsCount(), 1); + EXPECT_EQ(tx1.GetSaplingOutputsCount(), 2); EXPECT_EQ(tx1.GetValueBalanceSapling(), -4000); CWalletTx wtx {&wallet, tx1}; @@ -2269,17 +2272,23 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { wtx = wallet.mapWallet[hash]; // Prepare to spend the note that was just created - auto maybe_pt = wtx.DecryptSaplingNote(Params(), SaplingOutPoint(hash, 0)); + auto outpt = SaplingOutPoint(hash, 0); + auto maybe_pt = wtx.DecryptSaplingNote(Params(), outpt); + if (!static_cast(maybe_pt)) { + outpt = SaplingOutPoint(hash, 1); + maybe_pt = wtx.DecryptSaplingNote(Params(), outpt); + } ASSERT_EQ(static_cast(maybe_pt), true); auto maybe_note = maybe_pt.value().first.note(ivk); ASSERT_EQ(static_cast(maybe_note), true); auto note = maybe_note.value(); auto anchor = frontiers.sapling.root(); - auto witness = frontiers.sapling.witness(); + auto witness = wtx.mapSaplingNoteData[outpt].witnesses.back(); + ASSERT_EQ(anchor, witness.root()); // Create a Sapling-only transaction // 0.0004 z-ZEC in, 0.00025 z-ZEC out, default fee, 0.00005 z-ZEC change - auto builder2 = TransactionBuilder(Params(), 2, std::nullopt); + auto builder2 = TransactionBuilder(Params(), 2, std::nullopt, anchor); builder2.AddSaplingSpend(sk, note, witness); builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 2500, {}); auto tx2 = builder2.Build().GetTxOrThrow(); diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 34ee9b15c8..15e121cbbd 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -48,7 +48,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { // manually add new spending key to wallet auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); - auto sk = m.Derive(0); + auto sk = m.Derive(0 | HARDENED_KEY_LIMIT); ASSERT_TRUE(wallet.AddSaplingZKey(sk)); // verify wallet did add it @@ -88,7 +88,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) { EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa)); // Load a third key into the wallet - auto sk2 = m.Derive(1); + auto sk2 = m.Derive(1 | HARDENED_KEY_LIMIT); ASSERT_TRUE(wallet.LoadSaplingZKey(sk2)); // attach metadata to this third key diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 0ed6cd9038..b64f8db12d 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -691,7 +691,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) BOOST_CHECK_EQUAL(retValue.get_str(), testKey); // create a random Sapling key locally; split between IVKs and spending keys. - auto testSaplingSpendingKey = m.Derive(i); + auto testSaplingSpendingKey = m.Derive(i | HARDENED_KEY_LIMIT); auto testSaplingPaymentAddress = testSaplingSpendingKey.ToXFVK().DefaultAddress(); if (i % 2 == 0) { std::string testSaplingAddr = keyIO.EncodePaymentAddress(testSaplingPaymentAddress); @@ -1204,7 +1204,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // Mutable tx containing contextual information we need to build tx UniValue retValue = CallRPC("getblockcount"); int nHeight = retValue.get_int(); - TransactionBuilder builder(Params(), nHeight + 1, std::nullopt, pwalletMain); + TransactionBuilder builder(Params(), nHeight + 1, std::nullopt, SaplingMerkleTree::empty_root(), pwalletMain); } BOOST_AUTO_TEST_CASE(asyncrpcoperation_sign_send_raw_transaction) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cb0bfdc833..1bcb749b42 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1481,7 +1481,11 @@ void CWallet::RunSaplingMigration(int blockHeight) { lastOperation->cancel(); } pendingSaplingMigrationTxs.clear(); - std::shared_ptr operation(new AsyncRPCOperation_saplingmigration(blockHeight + 5)); + auto targetHeight = blockHeight + 5; + auto anchorBlockIndex = chainActive[blockHeight - 5]; + assert(anchorBlockIndex != nullptr); + auto saplingAnchor = anchorBlockIndex->hashFinalSaplingRoot; + std::shared_ptr operation(new AsyncRPCOperation_saplingmigration(targetHeight, saplingAnchor)); saplingMigrationOperationId = operation->getId(); q->addOperation(operation); } else if (blockHeight % 500 == 499) { diff --git a/src/wallet/wallet_tx_builder.cpp b/src/wallet/wallet_tx_builder.cpp index 6b84589cea..1e52df0bbd 100644 --- a/src/wallet/wallet_tx_builder.cpp +++ b/src/wallet/wallet_tx_builder.cpp @@ -937,9 +937,6 @@ TransactionBuilderResult TransactionEffects::ApproveAndBuild( orchardAnchor = anchorBlockIndex->hashFinalOrchardRoot; } - auto builder = TransactionBuilder(params, nextBlockHeight, orchardAnchor, &wallet); - builder.SetFee(fee); - // Track the total of notes that we've added to the builder. This // shouldn't strictly be necessary, given `spendable.LimitToAmount` CAmount totalSpend = 0; @@ -984,6 +981,9 @@ TransactionBuilderResult TransactionEffects::ApproveAndBuild( } } + auto builder = TransactionBuilder(params, nextBlockHeight, orchardAnchor, saplingAnchor, &wallet); + builder.SetFee(fee); + // Add Orchard spends for (size_t i = 0; i < orchardSpendInfo.size(); i++) { auto spendInfo = std::move(orchardSpendInfo[i]); diff --git a/src/zcash/address/zip32.cpp b/src/zcash/address/zip32.cpp index 703d64f295..f696b317d4 100644 --- a/src/zcash/address/zip32.cpp +++ b/src/zcash/address/zip32.cpp @@ -55,28 +55,6 @@ std::optional diversifier_index_t::ToTransparentChildIndex() const { } } -// -// SaplingExtendedFullViewingKey -// - -std::optional SaplingExtendedFullViewingKey::Derive(uint32_t i) const -{ - CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION); - ss_p << *this; - std::array p_bytes; - std::copy(ss_p.begin(), ss_p.end(), p_bytes.begin()); - - try { - auto i_bytes = sapling::zip32::xfvk_derive(p_bytes, i); - CDataStream ss_i(i_bytes, SER_NETWORK, PROTOCOL_VERSION); - SaplingExtendedFullViewingKey xfvk_i; - ss_i >> xfvk_i; - return xfvk_i; - } catch (rust::Error) { - return std::nullopt; - } -} - // // SaplingDiversifiableFullViewingKey // @@ -173,6 +151,10 @@ SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Master(const HDSeed& seed SaplingExtendedSpendingKey SaplingExtendedSpendingKey::Derive(uint32_t i) const { + if (i < HARDENED_KEY_LIMIT) { + throw std::runtime_error("non-hardened derivation is unsupported"); + } + CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION); ss_p << *this; std::array p_bytes; diff --git a/src/zcash/address/zip32.h b/src/zcash/address/zip32.h index c6ba85dda7..185f34602c 100644 --- a/src/zcash/address/zip32.h +++ b/src/zcash/address/zip32.h @@ -257,8 +257,6 @@ class SaplingExtendedFullViewingKey: public SaplingDiversifiableFullViewingKey { READWRITE(dk); } - std::optional Derive(uint32_t i) const; - friend inline bool operator==(const SaplingExtendedFullViewingKey& a, const SaplingExtendedFullViewingKey& b) { return ( a.depth == b.depth && diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index e784ace1f6..cef9ea61c9 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -759,6 +759,7 @@ double benchmark_create_sapling_spend() auto maybe_cmu = note.cmu(); tree.append(maybe_cmu.value()); auto witness = tree.witness(); + auto anchor = tree.root().GetRawBytes(); CDataStream ssExtSk(SER_NETWORK, PROTOCOL_VERSION); ssExtSk << sk; @@ -769,10 +770,9 @@ double benchmark_create_sapling_spend() std::move(ss.begin(), ss.end(), witnessChars.begin()); auto nHeight = Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; - auto builder = sapling::new_builder(*Params().RustNetwork(), nHeight); + auto builder = sapling::new_builder(*Params().RustNetwork(), nHeight, anchor, false); builder->add_spend( {reinterpret_cast(ssExtSk.data()), ssExtSk.size()}, - note.d, address.GetRawBytes(), note.value(), note.rcm().GetRawBytes(), @@ -781,7 +781,7 @@ double benchmark_create_sapling_spend() struct timeval tv_start; timer_start(tv_start); - auto result = sapling::build_bundle(std::move(builder), nHeight); + auto result = sapling::build_bundle(std::move(builder)); double t = timer_stop(tv_start); return t; @@ -791,9 +791,10 @@ double benchmark_create_sapling_output() { auto sk = libzcash::SaplingSpendingKey::random(); auto address = sk.default_address(); + auto anchor = SaplingMerkleTree::empty_root().GetRawBytes(); auto nHeight = Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; - auto builder = sapling::new_builder(*Params().RustNetwork(), nHeight); + auto builder = sapling::new_builder(*Params().RustNetwork(), nHeight, anchor, false); builder->add_recipient( uint256().GetRawBytes(), address.GetRawBytes(), @@ -803,7 +804,7 @@ double benchmark_create_sapling_output() struct timeval tv_start; timer_start(tv_start); - auto result = sapling::build_bundle(std::move(builder), nHeight); + auto result = sapling::build_bundle(std::move(builder)); double t = timer_stop(tv_start); return t;