Merge pull request #5917 from nuttycom/feature/wallet_orchard-checkpoint_depth

Select anchors at `-anchorconfirmations` depth.
This commit is contained in:
Kris Nuttycombe 2022-05-09 08:29:55 -06:00 committed by GitHub
commit 5c1a99a288
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 546 additions and 225 deletions

View File

@ -4,9 +4,24 @@ linker = "aarch64-linux-gnu-gcc"
[source.crates-io]
replace-with = "vendored-sources"
[source."https://github.com/zcash/halo2.git"]
git = "https://github.com/zcash/halo2.git"
rev = "0c33fa4e6e41464884765c8fb4cefebafd300ca2"
replace-with = "vendored-sources"
[source."https://github.com/zcash/incrementalmerkletree.git"]
git = "https://github.com/zcash/incrementalmerkletree.git"
rev = "f23e3d89507849a24543121839eea6f40b141aff"
replace-with = "vendored-sources"
[source."https://github.com/zcash/librustzcash.git"]
git = "https://github.com/zcash/librustzcash.git"
rev = "d14e7a707ce01cefcbc82651dad48f002185dded"
rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
replace-with = "vendored-sources"
[source."https://github.com/zcash/orchard.git"]
git = "https://github.com/zcash/orchard.git"
rev = "a30caec124aa6c6e7818b5100293204425c49de3"
replace-with = "vendored-sources"
[source."https://github.com/nuttycom/hdwallet.git"]

204
Cargo.lock generated
View File

@ -139,7 +139,7 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0944d18a9a37691b87733b39c9360c9950af9aa5f97e2455bc108d8eb64fc1c1"
dependencies = [
"bitvec",
"bitvec 0.22.3",
"blake2s_simd 0.5.11",
"byteorder",
"crossbeam-channel",
@ -154,16 +154,6 @@ dependencies = [
"subtle",
]
[[package]]
name = "bigint"
version = "4.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def"
dependencies = [
"byteorder",
"crunchy 0.1.6",
]
[[package]]
name = "bip0039"
version = "0.9.0"
@ -190,10 +180,22 @@ version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527"
dependencies = [
"funty",
"radium",
"funty 1.2.0",
"radium 0.6.2",
"tap",
"wyz",
"wyz 0.4.0",
]
[[package]]
name = "bitvec"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b"
dependencies = [
"funty 2.0.0",
"radium 0.7.0",
"tap",
"wyz 0.5.0",
]
[[package]]
@ -291,6 +293,12 @@ version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]]
name = "byte-slice-cast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -428,12 +436,6 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crunchy"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
[[package]]
name = "crunchy"
version = "0.2.2"
@ -562,7 +564,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "equihash"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"blake2b_simd",
"byteorder",
@ -571,7 +573,7 @@ dependencies = [
[[package]]
name = "f4jumble"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"blake2b_simd",
]
@ -582,11 +584,23 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e"
dependencies = [
"bitvec",
"bitvec 0.22.3",
"rand_core 0.6.3",
"subtle",
]
[[package]]
name = "fixed-hash"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c"
dependencies = [
"byteorder",
"rand 0.8.5",
"rustc-hex",
"static_assertions",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -613,6 +627,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures-channel"
version = "0.3.21"
@ -719,11 +739,10 @@ dependencies = [
[[package]]
name = "halo2_gadgets"
version = "0.1.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7524b798b8b3689a198cd87ee1d22fe3ca007a51d35c4093f32d75c0efc30abe"
source = "git+https://github.com/zcash/halo2.git?rev=0c33fa4e6e41464884765c8fb4cefebafd300ca2#0c33fa4e6e41464884765c8fb4cefebafd300ca2"
dependencies = [
"arrayvec 0.7.2",
"bitvec",
"bitvec 0.22.3",
"ff",
"group",
"halo2_proofs",
@ -737,11 +756,9 @@ dependencies = [
[[package]]
name = "halo2_proofs"
version = "0.1.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0240b05b791cccfd6451b010b19711280e63b87f495bd84df0103f35c9139e7"
source = "git+https://github.com/zcash/halo2.git?rev=0c33fa4e6e41464884765c8fb4cefebafd300ca2#0c33fa4e6e41464884765c8fb4cefebafd300ca2"
dependencies = [
"blake2b_simd",
"bumpalo",
"ff",
"group",
"pasta_curves",
@ -851,11 +868,30 @@ 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",
]
[[package]]
name = "incrementalmerkletree"
version = "0.3.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5812f2cfa06a7694b842402e9a100529d80fdc3022ead65ad98ce0af0bcd3311"
source = "git+https://github.com/zcash/incrementalmerkletree.git?rev=f23e3d89507849a24543121839eea6f40b141aff#f23e3d89507849a24543121839eea6f40b141aff"
dependencies = [
"serde",
]
@ -906,7 +942,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7baec19d4e83f9145d4891178101a604565edff9645770fc979804138b04c"
dependencies = [
"bitvec",
"bitvec 0.22.3",
"bls12_381",
"ff",
"group",
@ -1252,11 +1288,10 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "orchard"
version = "0.1.0-beta.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bb8c5f5a0977683b5071650f1e7d91f296e344a796fda585086d5b4b0a74ea"
source = "git+https://github.com/zcash/orchard.git?rev=a30caec124aa6c6e7818b5100293204425c49de3#a30caec124aa6c6e7818b5100293204425c49de3"
dependencies = [
"aes",
"bitvec",
"bitvec 0.22.3",
"blake2b_simd",
"ff",
"fpe",
@ -1294,6 +1329,32 @@ dependencies = [
"group",
]
[[package]]
name = "parity-scale-codec"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8b44461635bbb1a0300f100a841e571e7d919c81c73075ef5d152ffdb521066"
dependencies = [
"arrayvec 0.7.2",
"bitvec 1.0.0",
"byte-slice-cast",
"impl-trait-for-tuples",
"parity-scale-codec-derive",
"serde",
]
[[package]]
name = "parity-scale-codec-derive"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
@ -1422,6 +1483,27 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "primitive-types"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a"
dependencies = [
"fixed-hash",
"impl-codec",
"uint",
]
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
"thiserror",
"toml",
]
[[package]]
name = "proc-macro2"
version = "1.0.37"
@ -1462,6 +1544,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "radix_trie"
version = "0.2.1"
@ -1680,6 +1768,12 @@ version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc-hex"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -1930,6 +2024,15 @@ dependencies = [
"syn",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "tower-service"
version = "0.3.1"
@ -2011,12 +2114,12 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "uint"
version = "0.9.1"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
dependencies = [
"byteorder",
"crunchy 0.2.2",
"crunchy",
"hex",
"static_assertions",
]
@ -2198,10 +2301,19 @@ dependencies = [
"tap",
]
[[package]]
name = "wyz"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e"
dependencies = [
"tap",
]
[[package]]
name = "zcash_address"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"bech32",
"bs58",
@ -2212,7 +2324,7 @@ dependencies = [
[[package]]
name = "zcash_encoding"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"byteorder",
"nonempty",
@ -2221,17 +2333,17 @@ dependencies = [
[[package]]
name = "zcash_history"
version = "0.2.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"bigint",
"blake2b_simd",
"byteorder",
"primitive-types",
]
[[package]]
name = "zcash_note_encryption"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"chacha20",
"chacha20poly1305",
@ -2242,11 +2354,11 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"aes",
"bip0039",
"bitvec",
"bitvec 0.22.3",
"blake2b_simd",
"blake2s_simd 1.0.0",
"bls12_381",
@ -2278,7 +2390,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=d14e7a707ce01cefcbc82651dad48f002185dded#d14e7a707ce01cefcbc82651dad48f002185dded"
source = "git+https://github.com/zcash/librustzcash.git?rev=68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b#68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b"
dependencies = [
"bellman",
"blake2b_simd",

View File

@ -84,10 +84,14 @@ panic = 'abort'
codegen-units = 1
[patch.crates-io]
halo2_gadgets = { git = "https://github.com/zcash/halo2.git", rev = "0c33fa4e6e41464884765c8fb4cefebafd300ca2" }
halo2_proofs = { git = "https://github.com/zcash/halo2.git", rev = "0c33fa4e6e41464884765c8fb4cefebafd300ca2" }
hdwallet = { git = "https://github.com/nuttycom/hdwallet.git", rev = "9b4c1bdbe0517e3a7a8f285d6048a37d472ba3bc" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "f23e3d89507849a24543121839eea6f40b141aff" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "a30caec124aa6c6e7818b5100293204425c49de3" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }

View File

@ -188,6 +188,14 @@ Files: depends/*/vendored-sources/instant/*
Copyright: 2019 Sébastien Crozet
License: BSD-3-clause
Files: depends/*/vendored-sources/parity-scale-codec/*
Copyright: 2018-2022 Parity Technologies
License: Apache-2.0
Files: depends/*/vendored-sources/parity-scale-codec-derive/*
Copyright: 2018-2022 Parity Technologies
License: Apache-2.0
Files: depends/*/vendored-sources/subtle/*
Copyright: 2016-2017 isis agora lovecruft, Henry de Valence
License: BSD-3-clause

View File

@ -57,6 +57,21 @@ Option handling
- The `-reindex` and `-reindex-chainstate` options now imply `-rescan`
(provided that the wallet is enabled and pruning is disabled, and unless
`-rescan=0` is specified explicitly).
- A new `-anchorconfirmations` argument has been added to allow the user
to specify the number of blocks back from the chain tip that anchors will be
selected from when spending notes. By default, anchors will now be selected
to have 3 confirmations. Values greater than 100 are not supported.
RPC Interface
-------------
- The default `minconf` value for `z_sendmany` is now 10 confirmations instead
of 1. If `minconf` and specifies a value less than that provided for
`-anchorconfirmations`, it will also override that value as it is not
possible to spend notes that are more recent than the anchor. Selecting
`minconf` values less than 3 is not recommended, as it allows the transaction
to be distinguished from transactions using the default for
`-anchorconfirmations`.
RPC Changes
-----------

View File

@ -89,9 +89,9 @@ class MempoolLimit(BitcoinTestFramework):
print("Checking mempool size reset after block mined...")
self.check_mempool_sizes(0)
zaddr4 = self.nodes[0].z_getnewaddress('sapling')
opid4 = self.nodes[0].z_sendmany(zaddr1, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}])
opid4 = self.nodes[0].z_sendmany(zaddr1, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}], 1)
wait_and_assert_operationid_status(self.nodes[0], opid4)
opid5 = self.nodes[0].z_sendmany(zaddr2, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}])
opid5 = self.nodes[0].z_sendmany(zaddr2, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}], 1)
wait_and_assert_operationid_status(self.nodes[0], opid5)
self.sync_all()

View File

@ -93,7 +93,7 @@ class MempoolTxExpiryTest(BitcoinTestFramework):
zsendamount = Decimal('1.0') - DEFAULT_FEE
recipients = []
recipients.append({"address": z_bob, "amount": zsendamount})
myopid = self.nodes[0].z_sendmany(z_alice, recipients)
myopid = self.nodes[0].z_sendmany(z_alice, recipients, 1)
persist_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
persist_transparent = self.nodes[0].sendtoaddress(bob, 0.01)
# Verify transparent transaction is version 4 intended for Sapling branch
@ -157,7 +157,7 @@ class MempoolTxExpiryTest(BitcoinTestFramework):
self.split_network()
print("\nBlockheight advances to equal expiry block height. After reorg, txs should persist in mempool")
myopid = self.nodes[0].z_sendmany(z_alice, recipients)
myopid = self.nodes[0].z_sendmany(z_alice, recipients, 1)
persist_shielded_2 = wait_and_assert_operationid_status(self.nodes[0], myopid)
persist_transparent_2 = self.nodes[0].sendtoaddress(bob, 0.01)
rawtx_trans = self.nodes[0].getrawtransaction(persist_transparent_2, 1)
@ -192,7 +192,7 @@ class MempoolTxExpiryTest(BitcoinTestFramework):
print("\nBlockheight advances to greater than expiry block height. After reorg, txs should expire from mempool")
print("Balance before expire_shielded is sent: ", self.nodes[0].z_gettotalbalance())
myopid = self.nodes[0].z_sendmany(z_alice, recipients)
myopid = self.nodes[0].z_sendmany(z_alice, recipients, 1)
expire_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01)
print("Blockheight node 0 at expire_transparent creation:", self.nodes[0].getblockchaininfo()['blocks'])
@ -223,7 +223,7 @@ class MempoolTxExpiryTest(BitcoinTestFramework):
print("\nBlockheight advances to just before expiring soon threshold. Txs should be rejected from entering mempool.")
print("Balance before expire_shielded is sent: ", self.nodes[0].z_gettotalbalance())
myopid = self.nodes[0].z_sendmany(z_alice, recipients)
myopid = self.nodes[0].z_sendmany(z_alice, recipients, 1)
expire_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01)
print("Blockheight node 0 at expire_transparent creation:", self.nodes[0].getblockchaininfo()['blocks'])

View File

@ -12,7 +12,8 @@ from mergetoaddress_helper import assert_mergetoaddress_exception
class MergeToAddressMixedNotes(BitcoinTestFramework):
def setup_nodes(self):
return start_nodes(4, self.options.tmpdir)
self.num_nodes = 4
return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[['-anchorconfirmations=1']] * self.num_nodes)
def setup_chain(self):
print("Initializing test directory " + self.options.tmpdir)

View File

@ -16,7 +16,7 @@ class MergeToAddressSapling (BitcoinTestFramework):
self.helper.setup_chain(self)
def setup_network(self, split=False):
self.helper.setup_network(self)
self.helper.setup_network(self, ['-anchorconfirmations=1'])
def run_test(self):
self.helper.run_test(self)

View File

@ -16,7 +16,7 @@ class MergeToAddressSprout (BitcoinTestFramework):
self.helper.setup_chain(self)
def setup_network(self, split=False):
self.helper.setup_network(self)
self.helper.setup_network(self, ['-anchorconfirmations=1'])
def run_test(self):
self.helper.run_test(self)

View File

@ -20,8 +20,7 @@ class RegtestSignrawtransactionTest (BitcoinTestFramework):
# Create and sign Sapling transaction.
# If the incorrect consensus branch id is selected, there will be a signing error.
opid = self.nodes[1].z_sendmany(taddr,
[{'address': zaddr1, 'amount': 1}])
opid = self.nodes[1].z_sendmany(taddr, [{'address': zaddr1, 'amount': 1}], 1)
wait_and_assert_operationid_status(self.nodes[1], opid)
if __name__ == '__main__':

View File

@ -20,7 +20,9 @@ from test_framework.util import (
import logging
HAS_CANOPY = ['-nurejectoldversions=false',
HAS_CANOPY = [
'-nurejectoldversions=false',
'-anchorconfirmations=1',
nuparams(BLOSSOM_BRANCH_ID, 205),
nuparams(HEARTWOOD_BRANCH_ID, 210),
nuparams(CANOPY_BRANCH_ID, 220),

View File

@ -142,7 +142,7 @@ class ListReceivedTest (BitcoinTestFramework):
taddr = self.nodes[1].getnewaddress()
# Generate some change by sending part of zaddr1 back to taddr
opid = self.nodes[1].z_sendmany(zaddr1, [{'address': taddr, 'amount': 0.6}])
opid = self.nodes[1].z_sendmany(zaddr1, [{'address': taddr, 'amount': 0.6}], 1)
txid = wait_and_assert_operationid_status(self.nodes[1], opid)
self.generate_and_sync(height+4)
@ -220,7 +220,7 @@ class ListReceivedTest (BitcoinTestFramework):
opid = self.nodes[1].z_sendmany(taddr, [
{'address': zaddr1, 'amount': 1, 'memo': my_memo},
{'address': zaddrExt, 'amount': 2},
])
], 1)
txid = wait_and_assert_operationid_status(self.nodes[1], opid)
self.sync_all()
@ -287,8 +287,7 @@ class ListReceivedTest (BitcoinTestFramework):
# Generate some change by sending part of zaddr1 to zaddr2
txidPrev = txid
zaddr2 = self.nodes[1].z_getnewaddress('sapling')
opid = self.nodes[1].z_sendmany(zaddr1,
[{'address': zaddr2, 'amount': 0.6}])
opid = self.nodes[1].z_sendmany(zaddr1, [{'address': zaddr2, 'amount': 0.6}], 1)
txid = wait_and_assert_operationid_status(self.nodes[1], opid)
self.sync_all()
self.generate_and_sync(height+4)
@ -404,7 +403,7 @@ class ListReceivedTest (BitcoinTestFramework):
assert_equal(len(r), 0, "unified_addr should have received zero notes")
# Create a note in this UA on node1
opid = node.z_sendmany(zaddr1, [{'address': unified_addr, 'amount': 0.1}])
opid = node.z_sendmany(zaddr1, [{'address': unified_addr, 'amount': 0.1}], 1)
txid_sapling = wait_and_assert_operationid_status(node, opid)
self.generate_and_sync(height+5)
@ -499,7 +498,7 @@ class ListReceivedTest (BitcoinTestFramework):
opid = self.nodes[1].z_sendmany(uao, [
{'address': uaso, 'amount': Decimal('0.3')},
{'address': ua_node0, 'amount': Decimal('0.2')}
])
], 1)
txid1 = wait_and_assert_operationid_status(self.nodes[1], opid)
self.sync_all()

View File

@ -53,7 +53,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
recipients = []
recipients.append({"address":myzaddr, "amount":7.0})
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients), timeout=120)
wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients, 1), timeout=120)
self.sync_all()
self.nodes[0].generate(1)
@ -71,7 +71,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
recipients = []
recipients.append({"address":myzaddr3, "amount":2.0})
wait_and_assert_operationid_status(self.nodes[2], self.nodes[2].z_sendmany(myzaddr, recipients), timeout=120)
wait_and_assert_operationid_status(self.nodes[2], self.nodes[2].z_sendmany(myzaddr, recipients, 1), timeout=120)
self.sync_all()
self.nodes[2].generate(1)
@ -93,7 +93,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
recipients = []
recipients.append({"address":mytaddr1, "amount":1.0})
wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients), timeout=120)
wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients, 1), timeout=120)
self.sync_all()
self.nodes[1].generate(1)

View File

@ -32,6 +32,10 @@ class WalletOrchardPersistenceTest(BitcoinTestFramework):
# Sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 200)
# activate NU5
self.nodes[0].generate(1)
self.sync_all()
# Send some Orchard funds to node 2 for later spending after we split the network
acct0 = self.nodes[0].z_getnewaccount()['account']
ua0 = self.nodes[0].z_getaddressforaccount(acct0, ['sapling', 'orchard'])['address']
@ -40,7 +44,7 @@ class WalletOrchardPersistenceTest(BitcoinTestFramework):
myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[0], myopid)
# Mine the tx & activate NU5
# Mine the tx
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()

View File

@ -53,6 +53,7 @@ class WalletSendManyAnyTaddr(BitcoinTestFramework):
{'address': node3taddr1, 'amount': 60},
{'address': node3taddr2, 'amount': 75},
],
1
),
)
self.sync_all()
@ -67,7 +68,7 @@ class WalletSendManyAnyTaddr(BitcoinTestFramework):
# We should be able to spend multiple UTXOs at once
wait_and_assert_operationid_status(
self.nodes[3],
self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 100}]),
self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 100}], 1),
)
self.sync_all()
@ -84,7 +85,7 @@ class WalletSendManyAnyTaddr(BitcoinTestFramework):
# Send from a change t-address.
wait_and_assert_operationid_status(
self.nodes[3],
self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 20}]),
self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 20}], 1),
)
self.sync_all()
@ -95,7 +96,7 @@ class WalletSendManyAnyTaddr(BitcoinTestFramework):
assert_equal(self.nodes[1].z_getbalance(recipient), 120)
# Check that ANY_TADDR note selection doesn't attempt a double-spend
myopid = self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 20}])
myopid = self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 20}], 1)
wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient funds: have 14.99998, need 20.00001")
# Create an expired transaction on node 3.

View File

@ -107,7 +107,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
assert_equal(error_result["method"], "z_sendmany")
params = error_result["params"]
assert_equal(params["fee"], DEFAULT_FEE) # default
assert_equal(params["minconf"], Decimal('1')) # default
assert_equal(params["minconf"], Decimal('10')) # default
assert_equal(params["fromaddress"], mytaddr)
assert_equal(params["amounts"][0]["address"], myzaddr)
assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))
@ -198,7 +198,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
unshieldvalue = Decimal('10.0')
recipients = []
recipients.append({"address":mytaddr, "amount": unshieldvalue})
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1)
mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
assert(mytxid is not None)
self.sync_all()
@ -224,7 +224,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
recipients = []
amount = Decimal('10.0') - DEFAULT_FEE - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold
recipients.append({"address":self.nodes[0].getnewaddress(), "amount":amount })
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
myopid = self.nodes[0].z_sendmany(mytaddr, recipients, 1)
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)")
# Send will fail because send amount is too big, even when including coinbase utxos
@ -238,9 +238,9 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# z_sendmany will fail because of insufficient funds
recipients = []
recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000.0')})
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
myopid = self.nodes[0].z_sendmany(mytaddr, recipients, 1)
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.")
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1)
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 9.99998, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.")
# Send will fail because of insufficient funds unless sender uses coinbase utxos
@ -276,7 +276,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
self.nodes[0].getinfo()
# Issue #2263 Workaround END
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1)
try:
wait_and_assert_operationid_status(self.nodes[0], myopid)
except JSONRPCException as e:

View File

@ -75,7 +75,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
recipients = []
recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('18.0')})
recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('12.0') - 4*DEFAULT_FEE})
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1)
# Wait for Tx 2 to begin executing...
for x in range(1, 60):

View File

@ -23,7 +23,7 @@ from decimal import Decimal
class WalletZSendmanyTest(BitcoinTestFramework):
def setup_network(self, split=False):
self.nodes = start_nodes(3, self.options.tmpdir, [[
nuparams(NU5_BRANCH_ID, 220),
nuparams(NU5_BRANCH_ID, 238),
]] * self.num_nodes)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@ -91,7 +91,7 @@ class WalletZSendmanyTest(BitcoinTestFramework):
# send from node 0 to node 2 taddr
mytxid = self.nodes[0].sendtoaddress(mytaddr, 10.0)
self.sync_all()
self.nodes[0].generate(1)
self.nodes[0].generate(10)
self.sync_all()
# send node 2 taddr to zaddr
@ -112,7 +112,7 @@ class WalletZSendmanyTest(BitcoinTestFramework):
assert_equal(Decimal(wallet_info["shielded_unconfirmed_balance"]), zsendmanynotevalue)
assert_equal(Decimal(wallet_info["shielded_balance"]), Decimal('0.0'))
self.nodes[2].generate(1)
self.nodes[2].generate(10)
self.sync_all()
assert_equal(self.nodes[2].getbalance(), node2utxobalance)

View File

@ -362,7 +362,8 @@ void orchard_wallet_get_potential_spends_from_nullifier(
OrchardSpendInfoPtr* orchard_wallet_get_spend_info(
const OrchardWalletPtr* wallet,
const unsigned char txid[32],
uint32_t action_idx);
uint32_t action_idx,
const unsigned char as_of_root[32]);
/**
* Run the garbage collection operation on the wallet's note commitment

View File

@ -1,22 +1,26 @@
use byteorder::{ReadBytesExt, WriteBytesExt};
use std::collections::{BTreeMap, BTreeSet};
use std::io::{self, Read, Write};
use incrementalmerkletree::{
bridgetree::{BridgeTree, Checkpoint},
Hashable,
Hashable, Position,
};
use zcash_encoding::{Optional, Vector};
use zcash_primitives::merkle_tree::{
incremental::{
read_bridge_v1, read_leu64_usize, read_position, write_bridge_v1, write_position,
write_usize_leu64, SER_V1,
write_usize_leu64, SER_V1, SER_V2,
},
HashSer,
};
pub fn write_checkpoint_v1<W: Write>(mut writer: W, checkpoint: &Checkpoint) -> io::Result<()> {
pub fn write_checkpoint_v2<W: Write>(mut writer: W, checkpoint: &Checkpoint) -> io::Result<()> {
write_usize_leu64(&mut writer, checkpoint.bridges_len())?;
writer.write_u8(if checkpoint.is_witnessed() { 1 } else { 0 })?;
Vector::write_sized(&mut writer, checkpoint.witnessed().iter(), |w, p| {
write_position(w, *p)
})?;
Vector::write_sized(
&mut writer,
checkpoint.forgotten().iter(),
@ -29,25 +33,54 @@ pub fn write_checkpoint_v1<W: Write>(mut writer: W, checkpoint: &Checkpoint) ->
Ok(())
}
pub fn read_checkpoint_v1<R: Read>(mut reader: R) -> io::Result<Checkpoint> {
pub fn read_checkpoint_v1<R: Read>(
mut reader: R,
save_positions: &mut BTreeMap<usize, Position>,
) -> io::Result<Checkpoint> {
let bridges_len = read_leu64_usize(&mut reader)?;
let is_witnessed = reader.read_u8()? == 1;
let forgotten = Vector::read_collected(&mut reader, |mut r| {
Ok((read_position(&mut r)?, read_leu64_usize(&mut r)?))
})?;
// v1 checkpoints lacked information about the witnesses that were made during their
// span of blocks, so we assume that a witness was made in the first checkpoint
// with a corresponding bridge index.
let witnessed = if let Some(position) = save_positions.remove(&bridges_len) {
BTreeSet::from([position])
} else {
BTreeSet::new()
};
Ok(Checkpoint::from_parts(
bridges_len,
is_witnessed,
witnessed,
forgotten,
))
}
pub fn read_checkpoint_v2<R: Read>(mut reader: R) -> io::Result<Checkpoint> {
Ok(Checkpoint::from_parts(
read_leu64_usize(&mut reader)?,
reader.read_u8()? == 1,
Vector::read_collected(&mut reader, |r| read_position(r))?,
Vector::read_collected(&mut reader, |mut r| {
Ok((read_position(&mut r)?, read_leu64_usize(&mut r)?))
})?,
))
}
pub fn write_tree_v1<H: Hashable + HashSer + Ord, W: Write>(
pub fn write_tree<H: Hashable + HashSer + Ord, W: Write>(
mut writer: W,
tree: &BridgeTree<H, 32>,
) -> io::Result<()> {
Vector::write(&mut writer, tree.prior_bridges(), |w, b| {
write_bridge_v1(w, b)
writer.write_u8(SER_V2)?;
Vector::write(&mut writer, tree.prior_bridges(), |mut w, b| {
write_bridge_v1(&mut w, b)
})?;
Optional::write(&mut writer, tree.current_bridge().as_ref(), |w, b| {
write_bridge_v1(w, b)
Optional::write(&mut writer, tree.current_bridge().as_ref(), |mut w, b| {
write_bridge_v1(&mut w, b)
})?;
Vector::write_sized(
&mut writer,
@ -58,7 +91,7 @@ pub fn write_tree_v1<H: Hashable + HashSer + Ord, W: Write>(
},
)?;
Vector::write(&mut writer, tree.checkpoints(), |w, c| {
write_checkpoint_v1(w, c)
write_checkpoint_v2(w, c)
})?;
write_usize_leu64(&mut writer, tree.max_checkpoints())?;
@ -66,17 +99,35 @@ pub fn write_tree_v1<H: Hashable + HashSer + Ord, W: Write>(
}
#[allow(clippy::redundant_closure)]
pub fn read_tree_v1<H: Hashable + HashSer + Ord + Clone, R: Read>(
pub fn read_tree<H: Hashable + HashSer + Ord + Clone, R: Read>(
mut reader: R,
) -> io::Result<BridgeTree<H, 32>> {
let ser_version = reader.read_u8()?;
let prior_bridges = Vector::read(&mut reader, |r| read_bridge_v1(r))?;
let current_bridge = Optional::read(&mut reader, |r| read_bridge_v1(r))?;
let saved: BTreeMap<Position, usize> = Vector::read_collected(&mut reader, |mut r| {
Ok((read_position(&mut r)?, read_leu64_usize(&mut r)?))
})?;
let checkpoints = match ser_version {
SER_V1 => {
let mut save_positions = saved.iter().map(|(p, i)| (*i, *p)).collect();
Vector::read_collected_mut(&mut reader, |r| read_checkpoint_v1(r, &mut save_positions))
}
SER_V2 => Vector::read_collected(&mut reader, |r| read_checkpoint_v2(r)),
flag => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unrecognized tree serialization version: {:?}", flag),
)),
}?;
let max_checkpoints = read_leu64_usize(&mut reader)?;
BridgeTree::from_parts(
Vector::read(&mut reader, |r| read_bridge_v1(r))?,
Optional::read(&mut reader, |r| read_bridge_v1(r))?,
Vector::read_collected(&mut reader, |mut r| {
Ok((read_position(&mut r)?, read_leu64_usize(&mut r)?))
})?,
Vector::read(&mut reader, |r| read_checkpoint_v1(r))?,
read_leu64_usize(&mut reader)?,
prior_bridges,
current_bridge,
saved,
checkpoints,
max_checkpoints,
)
.map_err(|err| {
io::Error::new(
@ -88,23 +139,3 @@ pub fn read_tree_v1<H: Hashable + HashSer + Ord + Clone, R: Read>(
)
})
}
pub fn write_tree<H: Hashable + HashSer + Ord, W: Write>(
mut writer: W,
tree: &BridgeTree<H, 32>,
) -> io::Result<()> {
writer.write_u8(SER_V1)?;
write_tree_v1(&mut writer, tree)
}
pub fn read_tree<H: Hashable + HashSer + Ord + Clone, R: Read>(
mut reader: R,
) -> io::Result<BridgeTree<H, 32>> {
match reader.read_u8()? {
SER_V1 => read_tree_v1(&mut reader),
flag => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unrecognized tree serialization version: {:?}", flag),
)),
}
}

View File

@ -1,5 +1,5 @@
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use incrementalmerkletree::{bridgetree, bridgetree::BridgeTree, Frontier, Position, Tree};
use incrementalmerkletree::{bridgetree, bridgetree::BridgeTree, Position, Tree};
use libc::c_uchar;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryInto;
@ -201,6 +201,8 @@ pub enum SpendRetrievalError {
NoIvkForRecipient(Address),
FvkNotFound(IncomingViewingKey),
NoteNotPositioned(OutPoint),
InvalidMerkleRoot,
WitnessNotAvailableAtRoot(MerkleHashOrchard),
}
/// A struct used to return metadata about how a bundle was determined
@ -686,19 +688,23 @@ impl Wallet {
.collect()
}
pub fn note_commitment_tree_root(&self) -> MerkleHashOrchard {
self.witness_tree.root()
/// Returns the note of the Orchard note commitment tree, as of the specified checkpoint
/// depth. A depth of 0 corresponds to the chain tip.
pub fn note_commitment_tree_root(&self, checkpoint_depth: usize) -> Option<MerkleHashOrchard> {
self.witness_tree.root(checkpoint_depth)
}
/// Fetches the information necessary to spend the note at the given `OutPoint`,
/// relative to the current root known to the wallet of the Orchard commitment tree.
/// relative to the specified root of the Orchard note commitment tree.
///
/// Returns `None` if the `OutPoint` is not known to the wallet, or the Orchard bundle
/// containing the note has not been passed to `Wallet::append_bundle_commitments`.
/// containing the note had not been passed to `Wallet::append_bundle_commitments` at
/// the specified checkpoint depth.
#[tracing::instrument(level = "trace", skip(self))]
pub fn get_spend_info(
&self,
outpoint: OutPoint,
as_of_root: MerkleHashOrchard,
) -> Result<OrchardSpendInfo, SpendRetrievalError> {
// TODO: Take `confirmations` parameter and obtain the Merkle path to the root at
// that checkpoint, not the latest root.
@ -738,8 +744,8 @@ impl Wallet {
let path = self
.witness_tree
.authentication_path(*position)
.expect("wallet always has paths to positioned notes");
.authentication_path(*position, &as_of_root)
.ok_or(SpendRetrievalError::WitnessNotAvailableAtRoot(as_of_root))?;
Ok(OrchardSpendInfo::from_parts(
fvk.clone(),
@ -939,7 +945,9 @@ pub extern "C" fn orchard_wallet_commitment_tree_root(
let wallet = unsafe { wallet.as_ref() }.expect("Wallet pointer may not be null");
let root_ret = unsafe { root_ret.as_mut() }.expect("Cannot return to the null pointer.");
*root_ret = wallet.note_commitment_tree_root().to_bytes();
// there is always a valid note commitment tree root at depth 0
// (it may be the empty root)
*root_ret = wallet.note_commitment_tree_root(0).unwrap().to_bytes();
}
#[no_mangle]
@ -1213,13 +1221,23 @@ pub extern "C" fn orchard_wallet_get_spend_info(
wallet: *const Wallet,
txid: *const [c_uchar; 32],
action_idx: usize,
as_of_root: *const [c_uchar; 32],
) -> *mut OrchardSpendInfo {
let wallet = unsafe { wallet.as_ref() }.expect("Wallet pointer may not be null.");
let txid = TxId::from_bytes(*unsafe { txid.as_ref() }.expect("txid may not be null."));
let as_of_root = MerkleHashOrchard::from_bytes(
unsafe { as_of_root.as_ref() }.expect("note commitment tree root may not be null."),
);
let outpoint = OutPoint { txid, action_idx };
match wallet.get_spend_info(outpoint) {
let as_of_root = if as_of_root.is_some().into() {
Ok(as_of_root.unwrap())
} else {
Err(SpendRetrievalError::InvalidMerkleRoot)
};
match as_of_root.and_then(|root| wallet.get_spend_info(outpoint, root)) {
Ok(ret) => Box::into_raw(Box::new(ret)),
Err(e) => {
tracing::error!(

View File

@ -259,7 +259,8 @@ TransactionBuilder::TransactionBuilder(
nHeight(nHeight),
keystore(keystore),
coinsView(coinsView),
cs_coinsView(cs_coinsView)
cs_coinsView(cs_coinsView),
orchardAnchor(orchardAnchor)
{
mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
@ -292,6 +293,11 @@ bool TransactionBuilder::SupportsOrchard() const {
return orchardBuilder.has_value();
}
std::optional<uint256> TransactionBuilder::GetOrchardAnchor() const {
return orchardAnchor;
}
bool TransactionBuilder::AddOrchardSpend(
libzcash::OrchardSpendingKey sk,
orchard::SpendInfo spendInfo)

View File

@ -260,6 +260,7 @@ private:
CCriticalSection* cs_coinsView;
CMutableTransaction mtx;
CAmount fee = 10000;
std::optional<uint256> orchardAnchor;
std::optional<orchard::Builder> orchardBuilder;
CAmount valueBalanceOrchard = 0;
@ -297,6 +298,7 @@ public:
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)),
spends(std::move(builder.spends)),
@ -337,6 +339,8 @@ public:
bool SupportsOrchard() const;
std::optional<uint256> GetOrchardAnchor() const;
bool AddOrchardSpend(
libzcash::OrchardSpendingKey sk,
orchard::SpendInfo spendInfo);

View File

@ -327,7 +327,11 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
std::vector<std::optional<SaplingWitness>> witnesses;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetSaplingNoteWitnesses(saplingOPs, witnesses, anchor);
if (!pwalletMain->GetSaplingNoteWitnesses(saplingOPs, nAnchorConfirmations, witnesses, anchor)) {
// This error should not appear once we're nAnchorConfirmations blocks past
// Sapling activation.
throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sapling witnesses.");
};
}
// Add Sapling spends
@ -447,7 +451,11 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
std::vector<JSOutPoint> vOutPoints = {jso};
uint256 inputAnchor;
std::vector<std::optional<SproutWitness>> vInputWitnesses;
pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
if (!pwalletMain->GetSproutNoteWitnesses(vOutPoints, nAnchorConfirmations, vInputWitnesses, inputAnchor)) {
// This error should not appear once we're nAnchorConfirmations blocks past
// Sprout activation.
throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses.");
};
jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor};
}
}
@ -752,7 +760,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInf
uint256 anchor;
{
LOCK(cs_main);
pwalletMain->GetSproutNoteWitnesses(outPoints, witnesses, anchor);
if (!pwalletMain->GetSproutNoteWitnesses(outPoints, nAnchorConfirmations, witnesses, anchor)) {
// This error should not appear once we're nAnchorConfirmations blocks past
// Sprout activation.
throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses.");
}
}
return perform_joinsplit(info, witnesses, anchor);
}

View File

@ -137,12 +137,15 @@ bool AsyncRPCOperation_saplingmigration::main_impl() {
libzcash::SproutSpendingKey sproutSk;
pwalletMain->GetSproutSpendingKey(sproutEntry.address, sproutSk);
std::vector<JSOutPoint> vOutPoints = {sproutEntry.jsop};
// Each migration transaction SHOULD specify an anchor at height N-10
// Each migration transaction uses the anchor at height N-nAnchorConfirmations
// for each Sprout JoinSplit description
// TODO: the above functionality (in comment) is not implemented in zcashd
uint256 inputAnchor;
std::vector<std::optional<SproutWitness>> vInputWitnesses;
pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
if (!pwalletMain->GetSproutNoteWitnesses(vOutPoints, nAnchorConfirmations, vInputWitnesses, inputAnchor)) {
// This error should not appear once we're nAnchorConfirmations blocks past
// Sprout activation.
throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses.");
}
builder.AddSproutInput(sproutSk, sproutEntry.note, vInputWitnesses[0].value());
// Send change to the address of the first input
if (!changeAddr.has_value()) {

View File

@ -48,11 +48,12 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
ZTXOSelector ztxoSelector,
std::vector<SendManyRecipient> recipients,
int minDepth,
unsigned int anchorDepth,
TransactionStrategy strategy,
CAmount fee,
UniValue contextInfo) :
builder_(std::move(builder)), ztxoSelector_(ztxoSelector), recipients_(recipients),
mindepth_(minDepth), strategy_(strategy), fee_(fee),
mindepth_(minDepth), anchordepth_(anchorDepth), strategy_(strategy), fee_(fee),
contextinfo_(contextInfo)
{
assert(fee_ >= 0);
@ -496,8 +497,14 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> orchardSpendInfo;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, witnesses, anchor);
orchardSpendInfo = pwalletMain->GetOrchardSpendInfo(spendable.orchardNoteMetadata);
if (!pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, anchordepth_, witnesses, anchor)) {
// This error should not appear once we're nAnchorConfirmations blocks past
// Sapling activation.
throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sapling witnesses.");
}
if (builder_.GetOrchardAnchor().has_value()) {
orchardSpendInfo = pwalletMain->GetOrchardSpendInfo(spendable.orchardNoteMetadata, builder_.GetOrchardAnchor().value());
}
}
// Add Orchard spends
@ -582,7 +589,11 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
// inputAnchor is not needed by builder_.AddSproutInput as it is for Sapling.
uint256 inputAnchor;
pwalletMain->GetSproutNoteWitnesses(vOutPoints, vSproutWitnesses, inputAnchor);
if (!pwalletMain->GetSproutNoteWitnesses(vOutPoints, anchordepth_, vSproutWitnesses, inputAnchor)) {
// This error should not appear once we're nAnchorConfirmations blocks past
// Sprout activation.
throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses.");
}
}
// Add Sprout spends

View File

@ -48,6 +48,7 @@ public:
ZTXOSelector ztxoSelector,
std::vector<SendManyRecipient> recipients,
int minDepth,
unsigned int anchorDepth,
TransactionStrategy strategy,
CAmount fee = DEFAULT_FEE,
UniValue contextInfo = NullUniValue);
@ -72,6 +73,7 @@ private:
ZTXOSelector ztxoSelector_;
std::vector<SendManyRecipient> recipients_;
int mindepth_{1};
unsigned int anchordepth_{nAnchorConfirmations};
CAmount fee_;
UniValue contextinfo_; // optional data to include in return value from getStatus()

View File

@ -110,7 +110,7 @@ TEST(TransactionBuilder, OrchardToOrchard) {
// If we attempt to get spend info now, it will fail because the note hasn't
// been witnessed in the Orchard commitment tree.
EXPECT_THROW(wallet.GetSpendInfo(notes), std::logic_error);
EXPECT_THROW(wallet.GetSpendInfo(notes, wallet.GetLatestAnchor()), std::logic_error);
// Append the bundle to the wallet's commitment tree.
CBlock fakeBlock;
@ -119,7 +119,7 @@ TEST(TransactionBuilder, OrchardToOrchard) {
ASSERT_TRUE(wallet.AppendNoteCommitments(2, fakeBlock));
// Now we can get spend info for the note.
auto spendInfo = wallet.GetSpendInfo(notes);
auto spendInfo = wallet.GetSpendInfo(notes, wallet.GetLatestAnchor());
EXPECT_EQ(spendInfo[0].second.Value(), 40000);
// Get the root of the commitment tree.

View File

@ -288,7 +288,7 @@ TEST(WalletRPCTests, RPCZsendmanyTaddrToSapling)
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr, true, false).value();
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, pa, 1*COIN, "ABCD") };
TransactionStrategy strategy(PrivacyPolicy::AllowRevealedSenders);
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 0, strategy));
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 0, 0, strategy));
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
// Enable test mode so tx is not sent
@ -350,4 +350,4 @@ TEST(WalletRPCTests, RPCZsendmanyTaddrToSapling)
// Revert to default
RegtestDeactivateSapling();
UnloadGlobalWallet();
}
}

View File

@ -115,17 +115,19 @@ std::pair<JSOutPoint, SaplingOutPoint> CreateValidBlock(TestWallet& wallet,
return std::make_pair(jsoutpt, saplingNotes[0]);
}
std::pair<uint256, uint256> GetWitnessesAndAnchors(TestWallet& wallet,
std::vector<JSOutPoint>& sproutNotes,
std::vector<SaplingOutPoint>& saplingNotes,
std::pair<uint256, uint256> GetWitnessesAndAnchors(
const TestWallet& wallet,
const std::vector<JSOutPoint>& sproutNotes,
const std::vector<SaplingOutPoint>& saplingNotes,
const unsigned int anchorDepth,
std::vector<std::optional<SproutWitness>>& sproutWitnesses,
std::vector<std::optional<SaplingWitness>>& saplingWitnesses) {
sproutWitnesses.clear();
saplingWitnesses.clear();
uint256 sproutAnchor;
uint256 saplingAnchor;
wallet.GetSproutNoteWitnesses(sproutNotes, sproutWitnesses, sproutAnchor);
wallet.GetSaplingNoteWitnesses(saplingNotes, saplingWitnesses, saplingAnchor);
assert(wallet.GetSproutNoteWitnesses(sproutNotes, anchorDepth, sproutWitnesses, sproutAnchor));
assert(wallet.GetSaplingNoteWitnesses(saplingNotes, anchorDepth, saplingWitnesses, saplingAnchor));
return std::make_pair(sproutAnchor, saplingAnchor);
}
@ -884,8 +886,8 @@ TEST(WalletTests, GetConflictedOrchardNotes) {
auto recipient2 = ivk.Address(j2);
// Generate tx to spend note A
auto noteToSpend = std::move(wallet.GetOrchardSpendInfo(orchardEntries)[0]);
auto builder2 = TransactionBuilder(consensusParams, 2, orchardTree.root());
auto noteToSpend = std::move(wallet.GetOrchardSpendInfo(orchardEntries, orchardTree.root())[0]);
builder2.AddOrchardSpend(std::move(noteToSpend.first), std::move(noteToSpend.second));
auto maybeTx2 = builder2.Build();
EXPECT_TRUE(maybeTx2.IsTx());
@ -897,7 +899,7 @@ TEST(WalletTests, GetConflictedOrchardNotes) {
CWalletTx wtx2 {&wallet, tx2};
// Generate conflicting tx to spend note A
auto noteToSpend2 = std::move(wallet.GetOrchardSpendInfo(orchardEntries)[0]);
auto noteToSpend2 = std::move(wallet.GetOrchardSpendInfo(orchardEntries, orchardTree.root())[0]);
auto builder3 = TransactionBuilder(consensusParams, 2, orchardTree.root());
builder3.AddOrchardSpend(std::move(noteToSpend2.first), std::move(noteToSpend2.second));
auto maybeTx3 = builder3.Build();
@ -1374,7 +1376,7 @@ TEST(WalletTests, CachedWitnessesEmptyChain) {
std::vector<std::optional<SproutWitness>> sproutWitnesses;
std::vector<std::optional<SaplingWitness>> saplingWitnesses;
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, nAnchorConfirmations, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) sproutWitnesses[1]);
@ -1382,7 +1384,7 @@ TEST(WalletTests, CachedWitnessesEmptyChain) {
wallet.LoadWalletTx(wtx);
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, nAnchorConfirmations, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) sproutWitnesses[1]);
@ -1392,16 +1394,52 @@ TEST(WalletTests, CachedWitnessesEmptyChain) {
block.vtx.push_back(wtx);
CBlockIndex index(block);
MerkleFrontiers frontiers;
wallet.IncrementNoteWitnesses(Params().GetConsensus(), &index, &block, frontiers, true);
const auto& params = Params().GetConsensus();
wallet.IncrementNoteWitnesses(params, &index, &block, frontiers, true);
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
// this death will occur because there will not be sufficient Sprout witnesses to reach the
// default anchor depth
EXPECT_DEATH(::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, nAnchorConfirmations, sproutWitnesses, saplingWitnesses),
"GetSproutNoteWitnesses");
// add another block; we still don't have enough witnesses
{
CBlock another_block;
CBlockIndex another_index(another_block);
another_index.nHeight = 1;
wallet.IncrementNoteWitnesses(params, &another_index, &another_block, frontiers, true);
}
EXPECT_DEATH(::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, nAnchorConfirmations, sproutWitnesses, saplingWitnesses),
"GetSproutNoteWitnesses");
for (int i = 2; i <= 8; i++) {
CBlock another_block;
CBlockIndex another_index(another_block);
another_index.nHeight = i;
wallet.IncrementNoteWitnesses(params, &another_index, &another_block, frontiers, true);
}
CBlock last_block;
CBlockIndex last_index(last_block);
last_index.nHeight = 9;
wallet.IncrementNoteWitnesses(params, &last_index, &last_block, frontiers, true);
::GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, nAnchorConfirmations, sproutWitnesses, saplingWitnesses);
EXPECT_TRUE((bool) sproutWitnesses[0]);
EXPECT_TRUE((bool) sproutWitnesses[1]);
EXPECT_TRUE((bool) saplingWitnesses[0]);
for (int i = 9; i >= 1; i--) {
CBlock another_block;
CBlockIndex another_index(another_block);
another_index.nHeight = i;
wallet.DecrementNoteWitnesses(params, &another_index);
}
// Until #1302 is implemented, this should trigger an assertion
EXPECT_DEATH(wallet.DecrementNoteWitnesses(Params().GetConsensus(), &index),
EXPECT_DEATH(wallet.DecrementNoteWitnesses(params, &index),
".*nWitnessCacheSize > 0.*");
}
@ -1429,7 +1467,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
std::vector<std::optional<SproutWitness>> sproutWitnesses;
std::vector<std::optional<SaplingWitness>> saplingWitnesses;
anchors1 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
anchors1 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_NE(anchors1.first, anchors1.second);
}
@ -1451,7 +1489,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
std::vector<std::optional<SproutWitness>> sproutWitnesses;
std::vector<std::optional<SaplingWitness>> saplingWitnesses;
GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) saplingWitnesses[0]);
@ -1465,7 +1503,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
MerkleFrontiers frontiers2 = { .sprout = frontiers.sprout, .sapling = frontiers.sapling };
wallet.IncrementNoteWitnesses(Params().GetConsensus(), &index2, &block2, frontiers2, true);
auto anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_NE(anchors2.first, anchors2.second);
EXPECT_TRUE((bool) sproutWitnesses[0]);
@ -1475,7 +1513,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
// Decrementing should give us the previous anchor
wallet.DecrementNoteWitnesses(Params().GetConsensus(), &index2);
auto anchors3 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors3 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) saplingWitnesses[0]);
@ -1485,7 +1523,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
// Re-incrementing with the same block should give the same result
wallet.IncrementNoteWitnesses(Params().GetConsensus(), &index2, &block2, frontiers, true);
auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_NE(anchors4.first, anchors4.second);
EXPECT_TRUE((bool) sproutWitnesses[0]);
@ -1498,7 +1536,7 @@ TEST(WalletTests, CachedWitnessesChainTip) {
std::vector<std::optional<SproutWitness>> sproutWitnesses5;
std::vector<std::optional<SaplingWitness>> saplingWitnesses5;
auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses5, saplingWitnesses5);
auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses5, saplingWitnesses5);
EXPECT_NE(anchors5.first, anchors5.second);
EXPECT_EQ(sproutWitnesses, sproutWitnesses5);
@ -1540,7 +1578,7 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) {
std::vector<SaplingOutPoint> saplingNotes {outpts.second};
std::vector<std::optional<SproutWitness>> sproutWitnesses;
std::vector<std::optional<SaplingWitness>> saplingWitnesses;
anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
}
{
@ -1561,7 +1599,7 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) {
std::vector<std::optional<SproutWitness>> sproutWitnesses;
std::vector<std::optional<SaplingWitness>> saplingWitnesses;
auto anchors3 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors3 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) saplingWitnesses[0]);
@ -1570,7 +1608,7 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) {
// should give us the previous anchor
wallet.DecrementNoteWitnesses(Params().GetConsensus(), &index2);
auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors4 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) saplingWitnesses[0]);
@ -1581,7 +1619,7 @@ TEST(WalletTests, CachedWitnessesDecrementFirst) {
// Re-incrementing with the same block should give the same result
wallet.IncrementNoteWitnesses(Params().GetConsensus(), &index2, &block2, frontiers, true);
auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors5 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) saplingWitnesses[0]);
@ -1623,7 +1661,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) {
sproutNotes.push_back(outpts.first);
saplingNotes.push_back(outpts.second);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
for (size_t j = 0; j <= i; j++) {
EXPECT_TRUE((bool) sproutWitnesses[j]);
EXPECT_TRUE((bool) saplingWitnesses[j]);
@ -1638,7 +1676,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) {
MerkleFrontiers riPrevFrontiers{riFrontiers};
wallet.IncrementNoteWitnesses(Params().GetConsensus(), &(indices[i]), &(blocks[i]), riFrontiers, true);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) sproutWitnesses[j]);
EXPECT_TRUE((bool) saplingWitnesses[j]);
@ -1652,7 +1690,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) {
{
wallet.DecrementNoteWitnesses(Params().GetConsensus(), &(indices[i]));
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) sproutWitnesses[j]);
EXPECT_TRUE((bool) saplingWitnesses[j]);
@ -1664,7 +1702,7 @@ TEST(WalletTests, CachedWitnessesCleanIndex) {
{
wallet.IncrementNoteWitnesses(Params().GetConsensus(), &(indices[i]), &(blocks[i]), riPrevFrontiers, true);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) sproutWitnesses[j]);
EXPECT_TRUE((bool) saplingWitnesses[j]);
@ -1724,7 +1762,7 @@ TEST(WalletTests, ClearNoteWitnessCache) {
std::vector<std::optional<SaplingWitness>> saplingWitnesses;
// Before clearing, we should have a witness for one note
GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_TRUE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) sproutWitnesses[1]);
EXPECT_TRUE((bool) saplingWitnesses[0]);
@ -1735,7 +1773,7 @@ TEST(WalletTests, ClearNoteWitnessCache) {
// After clearing, we should not have a witness for either note
wallet.ClearNoteWitnessCache();
auto anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, sproutWitnesses, saplingWitnesses);
auto anchors2 = GetWitnessesAndAnchors(wallet, sproutNotes, saplingNotes, 1, sproutWitnesses, saplingWitnesses);
EXPECT_FALSE((bool) sproutWitnesses[0]);
EXPECT_FALSE((bool) sproutWitnesses[1]);
EXPECT_FALSE((bool) saplingWitnesses[0]);

View File

@ -13,14 +13,16 @@ std::optional<libzcash::OrchardSpendingKey> OrchardWallet::GetSpendingKeyForAddr
}
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> OrchardWallet::GetSpendInfo(
const std::vector<OrchardNoteMetadata>& noteMetadata) const
const std::vector<OrchardNoteMetadata>& noteMetadata,
uint256 anchor) const
{
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> result;
for (const auto& note : noteMetadata) {
auto pSpendInfo = orchard_wallet_get_spend_info(
inner.get(),
note.GetOutPoint().hash.begin(),
note.GetOutPoint().n);
note.GetOutPoint().n,
anchor.begin());
if (pSpendInfo == nullptr) {
throw std::logic_error("Called OrchardWallet::GetSpendInfo with unknown outpoint");
} else {

View File

@ -442,7 +442,8 @@ public:
}
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> GetSpendInfo(
const std::vector<OrchardNoteMetadata>& noteMetadata) const;
const std::vector<OrchardNoteMetadata>& noteMetadata,
uint256 anchor) const;
void GarbageCollect() {
orchard_wallet_gc_note_commitment_tree(inner.get());

View File

@ -48,6 +48,7 @@
#include <univalue.h>
#include <algorithm>
#include <numeric>
#include <optional>
#include <variant>
@ -4942,7 +4943,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
" \"amount\":amount (numeric, required) The numeric amount in " + CURRENCY_UNIT + " is the value\n"
" \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n"
" }, ... ]\n"
"3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n"
"3. minconf (numeric, optional, default=" + strprintf("%u", DEFAULT_NOTE_CONFIRMATIONS) + ") Only use funds confirmed at least this many times.\n"
"4. fee (numeric, optional, default=" + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n"
"5. privacyPolicy (string, optional, default=\"LegacyCompat\") Policy for what information leakage is acceptable.\n"
" One of the following strings:\n"
@ -5154,7 +5155,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
}
// Minimum confirmations
int nMinDepth = 1;
int nMinDepth = DEFAULT_NOTE_CONFIRMATIONS;
if (params.size() > 2) {
nMinDepth = params[2].get_int();
}
@ -5199,12 +5200,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
UniValue contextInfo = o;
std::optional<uint256> orchardAnchor;
if (!ztxoSelector.SelectsSprout()) {
// Allow Orchard recipients by setting an Orchard anchor.
// TODO: Add an orchardAnchorHeight field to ZTXOSelector so we can ensure the
// same anchor is used for witnesses of any selected Orchard note.
auto orchardAnchorHeight = nextBlockHeight - nOrchardAnchorConfirmations;
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
auto nAnchorDepth = std::min((unsigned int) nMinDepth, nAnchorConfirmations);
if (!ztxoSelector.SelectsSprout() && nAnchorDepth > 0) {
auto orchardAnchorHeight = nextBlockHeight - nAnchorDepth;
if (chainparams.GetConsensus().NetworkUpgradeActive(orchardAnchorHeight, Consensus::UPGRADE_NU5)) {
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
}
}
TransactionBuilder builder(chainparams.GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain);
@ -5212,7 +5213,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
std::shared_ptr<AsyncRPCOperation> operation(
new AsyncRPCOperation_sendmany(
std::move(builder), ztxoSelector, recipients, nMinDepth, strategy, nFee, contextInfo)
std::move(builder), ztxoSelector, recipients, nMinDepth, nAnchorDepth, strategy, nFee, contextInfo)
);
q->addOperation(operation);
AsyncRPCOperationId operationId = operation->getId();
@ -5560,8 +5561,10 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
std::optional<uint256> orchardAnchor;
if (canopyActive) {
// Allow Orchard recipients by setting an Orchard anchor.
auto orchardAnchorHeight = nextBlockHeight - nOrchardAnchorConfirmations;
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
auto orchardAnchorHeight = nextBlockHeight - nAnchorConfirmations;
if (Params().GetConsensus().NetworkUpgradeActive(orchardAnchorHeight, Consensus::UPGRADE_NU5)) {
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
}
}
TransactionBuilder builder = TransactionBuilder(
Params().GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain);
@ -5895,7 +5898,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
useAnySprout || useAnySapling ?
std::nullopt :
std::optional(NoteFilter::ForPaymentAddresses(zaddrs));
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter);
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, nAnchorConfirmations);
// If Sapling is not active, do not allow sending from a sapling addresses.
if (!saplingActive && saplingEntries.size() > 0) {
@ -6037,8 +6040,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
// Allow Orchard recipients by setting an Orchard anchor.
// TODO: Add an orchardAnchorHeight field to ZTXOSelector so we can ensure the
// same anchor is used for witnesses of any selected Orchard note.
auto orchardAnchorHeight = nextBlockHeight - nOrchardAnchorConfirmations;
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
auto orchardAnchorHeight = nextBlockHeight - nAnchorConfirmations;
if (Params().GetConsensus().NetworkUpgradeActive(orchardAnchorHeight, Consensus::UPGRADE_NU5)) {
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
}
}
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain);
}

View File

@ -1237,7 +1237,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, zaddr1, 100*COIN, "DEADBEEF") };
TransactionStrategy strategy;
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, strategy));
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy));
operation->main();
BOOST_CHECK(operation->isFailed());
std::string msg = operation->getErrorMessage();
@ -1250,7 +1250,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, taddr1, 100*COIN, "DEADBEEF") };
TransactionStrategy strategy(PrivacyPolicy::AllowRevealedRecipients);
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, strategy));
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy));
operation->main();
BOOST_CHECK(operation->isFailed());
std::string msg = operation->getErrorMessage();
@ -1263,7 +1263,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, zaddr1, 100*COIN, "DEADBEEF") };
TransactionStrategy strategy;
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, strategy));
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy));
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr);

View File

@ -51,7 +51,7 @@ unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
bool fPayAtLeastCustomFee = true;
unsigned int nOrchardAnchorConfirmations = DEFAULT_ORCHARD_ANCHOR_CONFIRMATIONS;
unsigned int nAnchorConfirmations = DEFAULT_ANCHOR_CONFIRMATIONS;
const char * DEFAULT_WALLET_DAT = "wallet.dat";
@ -3529,9 +3529,10 @@ bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const
return false;
}
void CWallet::GetSproutNoteWitnesses(const std::vector<JSOutPoint>& notes,
bool CWallet::GetSproutNoteWitnesses(const std::vector<JSOutPoint>& notes,
unsigned int confirmations,
std::vector<std::optional<SproutWitness>>& witnesses,
uint256 &final_anchor)
uint256 &final_anchor) const
{
LOCK(cs_wallet);
witnesses.resize(notes.size());
@ -3539,9 +3540,16 @@ void CWallet::GetSproutNoteWitnesses(const std::vector<JSOutPoint>& notes,
int i = 0;
for (JSOutPoint note : notes) {
if (mapWallet.count(note.hash) &&
mapWallet[note.hash].mapSproutNoteData.count(note) &&
mapWallet[note.hash].mapSproutNoteData[note].witnesses.size() > 0) {
witnesses[i] = mapWallet[note.hash].mapSproutNoteData[note].witnesses.front();
mapWallet.at(note.hash).mapSproutNoteData.count(note) &&
mapWallet.at(note.hash).mapSproutNoteData.at(note).witnesses.size() > 0) {
auto noteWitnesses = mapWallet.at(note.hash).mapSproutNoteData.at(note).witnesses;
auto it = noteWitnesses.cbegin(), end = noteWitnesses.cend();
for (int i = 1; i < confirmations; i++) {
if (it == end) return false;
++it;
}
if (it == end) return false;
witnesses[i] = *it;
if (!rt) {
rt = witnesses[i]->root();
} else {
@ -3554,11 +3562,13 @@ void CWallet::GetSproutNoteWitnesses(const std::vector<JSOutPoint>& notes,
if (rt) {
final_anchor = *rt;
}
return true;
}
void CWallet::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
bool CWallet::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
unsigned int confirmations,
std::vector<std::optional<SaplingWitness>>& witnesses,
uint256 &final_anchor)
uint256 &final_anchor) const
{
LOCK(cs_wallet);
witnesses.resize(notes.size());
@ -3566,9 +3576,16 @@ void CWallet::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
int i = 0;
for (SaplingOutPoint note : notes) {
if (mapWallet.count(note.hash) &&
mapWallet[note.hash].mapSaplingNoteData.count(note) &&
mapWallet[note.hash].mapSaplingNoteData[note].witnesses.size() > 0) {
witnesses[i] = mapWallet[note.hash].mapSaplingNoteData[note].witnesses.front();
mapWallet.at(note.hash).mapSaplingNoteData.count(note) &&
mapWallet.at(note.hash).mapSaplingNoteData.at(note).witnesses.size() > 0) {
auto noteWitnesses = mapWallet.at(note.hash).mapSaplingNoteData.at(note).witnesses;
auto it = noteWitnesses.cbegin(), end = noteWitnesses.cend();
for (int i = 1; i < confirmations; i++) {
if (it == end) return false;
++it;
}
if (it == end) return false;
witnesses[i] = *it;
if (!rt) {
rt = witnesses[i]->root();
} else {
@ -3581,13 +3598,15 @@ void CWallet::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
if (rt) {
final_anchor = *rt;
}
return true;
}
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> CWallet::GetOrchardSpendInfo(
const std::vector<OrchardNoteMetadata>& orchardNoteMetadata) const
const std::vector<OrchardNoteMetadata>& orchardNoteMetadata,
uint256 anchor) const
{
AssertLockHeld(cs_wallet);
return orchardWallet.GetSpendInfo(orchardNoteMetadata);
return orchardWallet.GetSpendInfo(orchardNoteMetadata, anchor);
}
isminetype CWallet::IsMine(const CTxIn &txin) const
@ -6574,12 +6593,15 @@ bool CWallet::ParameterInteraction(const CChainParams& params)
return UIError(_("-migrationdestaddress must be a valid Sapling address."));
}
}
if (mapArgs.count("-orchardanchorconfirmations")) {
int64_t confirmations = atoi64(mapArgs["-orchardanchorconfirmations"]);
if (mapArgs.count("-anchorconfirmations")) {
int64_t confirmations = atoi64(mapArgs["-anchorconfirmations"]);
if (confirmations < 1) {
return UIError(strprintf(_("Invalid value for -orchardanchorconfirmations='%u' (must be least 1)"), confirmations));
return UIError(strprintf(_("Invalid value for -anchorconfirmations='%u' (must be least 1)"), confirmations));
}
nOrchardAnchorConfirmations = confirmations;
if (confirmations > 100) {
return UIError(strprintf(_("Invalid value for -anchorconfirmations='%u' (must be at most 100)"), confirmations));
}
nAnchorConfirmations = confirmations;
}
return true;

View File

@ -53,7 +53,7 @@ extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
extern bool fPayAtLeastCustomFee;
extern unsigned int nOrchardAnchorConfirmations;
extern unsigned int nAnchorConfirmations;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
//! -paytxfee default
@ -78,8 +78,10 @@ static const unsigned int WITNESS_CACHE_SIZE = MAX_REORG_LENGTH + 1;
//! Amount of entropy used in generation of the mnemonic seed, in bytes.
static const size_t WALLET_MNEMONIC_ENTROPY_LENGTH = 32;
//! -orchardanchorconfirmations default
static const unsigned int DEFAULT_ORCHARD_ANCHOR_CONFIRMATIONS = 1;
//! -anchorconfirmations default
static const unsigned int DEFAULT_ANCHOR_CONFIRMATIONS = 3;
// Default minimum number of confirmations for note selection
static const unsigned int DEFAULT_NOTE_CONFIRMATIONS = 10;
extern const char * DEFAULT_WALLET_DAT;
@ -1778,16 +1780,19 @@ public:
bool IsSproutNullifierFromMe(const uint256& nullifier) const;
bool IsSaplingNullifierFromMe(const uint256& nullifier) const;
void GetSproutNoteWitnesses(
bool GetSproutNoteWitnesses(
const std::vector<JSOutPoint>& notes,
unsigned int confirmations,
std::vector<std::optional<SproutWitness>>& witnesses,
uint256 &final_anchor);
void GetSaplingNoteWitnesses(
uint256 &final_anchor) const;
bool GetSaplingNoteWitnesses(
const std::vector<SaplingOutPoint>& notes,
unsigned int confirmations,
std::vector<std::optional<SaplingWitness>>& witnesses,
uint256 &final_anchor);
uint256 &final_anchor) const;
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> GetOrchardSpendInfo(
const std::vector<OrchardNoteMetadata>& orchardNoteMetadata) const;
const std::vector<OrchardNoteMetadata>& orchardNoteMetadata,
uint256 anchor) const;
isminetype IsMine(const CTxIn& txin) const;
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
@ -1966,7 +1971,7 @@ public:
std::vector<SaplingNoteEntry>& saplingEntriesRet,
std::vector<OrchardNoteMetadata>& orchardNotesRet,
const std::optional<NoteFilter>& noteFilter,
int minDepth=1,
int minDepth,
int maxDepth=INT_MAX,
bool ignoreSpent=true,
bool requireSpendingKey=true,