Select Orchard anchors at `-orchardanchorconfirmations` depth.

This changes anchor selection and Orchard authentication path generation
to default to select anchors at a the depth specified by the
`-orchardanchorconfirmations` CLI argument, with a default anchor
selection depth of 10 blocks.

If the value of `minconf` used for a particular call to `z_sendmany` is
less than the the set number of Orchard anchor confirmations, `minconf`
will be used instead for the Orchard anchor confirmations depth,
so that the selected anchor will be certain to contain any notes
selected to be spent.

Fixes #5644
This commit is contained in:
Kris Nuttycombe 2022-04-28 16:37:42 -06:00
parent b7d6120b20
commit 63e4addd9a
26 changed files with 354 additions and 148 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

@ -11,6 +11,15 @@ Option handling
(provided that the wallet is enabled and pruning is disabled, and unless
`-rescan=0` is specified explicitly).
RPC Interface
-------------
- The default `minconf` value for `z_sendmany` is now 10 confirmations instead
of 1.
- When spending notes using `z_sendmany`, Orchard anchors are now selected at
a depth of 10 blocks instead of at the chain tip. If the `minconf` value is
less than 10, `minconf` is used for the anchor selection depth instead.
Build system
------------

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

@ -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

@ -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

@ -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, 240),
]] * 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,12 +688,12 @@ impl Wallet {
.collect()
}
pub fn note_commitment_tree_root(&self) -> MerkleHashOrchard {
self.witness_tree.root()
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`.
@ -699,6 +701,7 @@ impl Wallet {
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 +741,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 +942,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 +1218,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

@ -497,7 +497,9 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, witnesses, anchor);
orchardSpendInfo = pwalletMain->GetOrchardSpendInfo(spendable.orchardNoteMetadata);
if (builder_.GetOrchardAnchor().has_value()) {
orchardSpendInfo = pwalletMain->GetOrchardSpendInfo(spendable.orchardNoteMetadata, builder_.GetOrchardAnchor().value());
}
}
// Add Orchard spends

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

@ -884,8 +884,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 +897,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();

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

@ -47,6 +47,7 @@
#include <univalue.h>
#include <algorithm>
#include <numeric>
#include <optional>
#include <variant>
@ -1042,7 +1043,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
" \"address\":amount (numeric) The Zcash address is the key, the numeric amount in " + CURRENCY_UNIT + " is the value\n"
" ,...\n"
" }\n"
"3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
"3. minconf (numeric, optional, default=10) Only use the balance confirmed at least this many times.\n"
"4. \"comment\" (string, optional) A comment\n"
"5. subtractfeefromamount (string, optional) A json array with addresses.\n"
" The fee will be equally deducted from the amount of each selected address.\n"
@ -4857,6 +4858,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
if (fHelp || params.size() < 2 || params.size() > 5)
throw runtime_error(
strprintf(
"z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee ) ( privacyPolicy )\n"
"\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision."
"\nChange generated from one or more transparent addresses flows to a new transparent"
@ -4877,7 +4879,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=%u) 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"
@ -4905,6 +4907,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
+ HelpExampleCli("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]'")
+ HelpExampleCli("z_sendmany", "\"ANY_TADDR\" '[{\"address\": \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"amount\": 2.0}]'")
+ HelpExampleRpc("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]")
, nOrchardAnchorConfirmations)
);
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -5089,7 +5092,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
}
// Minimum confirmations
int nMinDepth = 1;
int nMinDepth = nOrchardAnchorConfirmations;
if (params.size() > 2) {
nMinDepth = params[2].get_int();
}
@ -5134,11 +5137,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
UniValue contextInfo = o;
std::optional<uint256> orchardAnchor;
if (!ztxoSelector.SelectsSprout()) {
if (!ztxoSelector.SelectsSprout() && nMinDepth > 0) {
// 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;
auto orchardAnchorHeight = nextBlockHeight - std::min((unsigned int) nMinDepth, nOrchardAnchorConfirmations);
orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
}
TransactionBuilder builder(chainparams.GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain);

View File

@ -3584,10 +3584,11 @@ void CWallet::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
}
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

View File

@ -79,7 +79,7 @@ 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;
static const unsigned int DEFAULT_ORCHARD_ANCHOR_CONFIRMATIONS = 10;
extern const char * DEFAULT_WALLET_DAT;
@ -1787,7 +1787,8 @@ public:
std::vector<std::optional<SaplingWitness>>& witnesses,
uint256 &final_anchor);
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;