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

View File

@ -84,10 +84,14 @@ panic = 'abort'
codegen-units = 1 codegen-units = 1
[patch.crates-io] [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" } hdwallet = { git = "https://github.com/nuttycom/hdwallet.git", rev = "9b4c1bdbe0517e3a7a8f285d6048a37d472ba3bc" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "f23e3d89507849a24543121839eea6f40b141aff" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" } orchard = { git = "https://github.com/zcash/orchard.git", rev = "a30caec124aa6c6e7818b5100293204425c49de3" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" } zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" } zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" } zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "68cbc2bb17b8967c7fb2f6034ac3d6174fc8f19b" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "d14e7a707ce01cefcbc82651dad48f002185dded" } 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 (provided that the wallet is enabled and pruning is disabled, and unless
`-rescan=0` is specified explicitly). `-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 Build system
------------ ------------

View File

@ -89,9 +89,9 @@ class MempoolLimit(BitcoinTestFramework):
print("Checking mempool size reset after block mined...") print("Checking mempool size reset after block mined...")
self.check_mempool_sizes(0) self.check_mempool_sizes(0)
zaddr4 = self.nodes[0].z_getnewaddress('sapling') 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) 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) wait_and_assert_operationid_status(self.nodes[0], opid5)
self.sync_all() self.sync_all()

View File

@ -93,7 +93,7 @@ class MempoolTxExpiryTest(BitcoinTestFramework):
zsendamount = Decimal('1.0') - DEFAULT_FEE zsendamount = Decimal('1.0') - DEFAULT_FEE
recipients = [] recipients = []
recipients.append({"address": z_bob, "amount": zsendamount}) 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_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
persist_transparent = self.nodes[0].sendtoaddress(bob, 0.01) persist_transparent = self.nodes[0].sendtoaddress(bob, 0.01)
# Verify transparent transaction is version 4 intended for Sapling branch # Verify transparent transaction is version 4 intended for Sapling branch
@ -157,7 +157,7 @@ class MempoolTxExpiryTest(BitcoinTestFramework):
self.split_network() self.split_network()
print("\nBlockheight advances to equal expiry block height. After reorg, txs should persist in mempool") 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_shielded_2 = wait_and_assert_operationid_status(self.nodes[0], myopid)
persist_transparent_2 = self.nodes[0].sendtoaddress(bob, 0.01) persist_transparent_2 = self.nodes[0].sendtoaddress(bob, 0.01)
rawtx_trans = self.nodes[0].getrawtransaction(persist_transparent_2, 1) 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("\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()) 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_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01) expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01)
print("Blockheight node 0 at expire_transparent creation:", self.nodes[0].getblockchaininfo()['blocks']) 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("\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()) 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_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01) expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01)
print("Blockheight node 0 at expire_transparent creation:", self.nodes[0].getblockchaininfo()['blocks']) 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. # Create and sign Sapling transaction.
# If the incorrect consensus branch id is selected, there will be a signing error. # If the incorrect consensus branch id is selected, there will be a signing error.
opid = self.nodes[1].z_sendmany(taddr, opid = self.nodes[1].z_sendmany(taddr, [{'address': zaddr1, 'amount': 1}], 1)
[{'address': zaddr1, 'amount': 1}])
wait_and_assert_operationid_status(self.nodes[1], opid) wait_and_assert_operationid_status(self.nodes[1], opid)
if __name__ == '__main__': if __name__ == '__main__':

View File

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

View File

@ -53,7 +53,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
recipients = [] recipients = []
recipients.append({"address":myzaddr, "amount":7.0}) 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.sync_all()
self.nodes[0].generate(1) self.nodes[0].generate(1)
@ -71,7 +71,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
recipients = [] recipients = []
recipients.append({"address":myzaddr3, "amount":2.0}) 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.sync_all()
self.nodes[2].generate(1) self.nodes[2].generate(1)
@ -93,7 +93,7 @@ class WalletNullifiersTest (BitcoinTestFramework):
recipients = [] recipients = []
recipients.append({"address":mytaddr1, "amount":1.0}) 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.sync_all()
self.nodes[1].generate(1) self.nodes[1].generate(1)

View File

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

View File

@ -107,7 +107,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
assert_equal(error_result["method"], "z_sendmany") assert_equal(error_result["method"], "z_sendmany")
params = error_result["params"] params = error_result["params"]
assert_equal(params["fee"], DEFAULT_FEE) # default 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["fromaddress"], mytaddr)
assert_equal(params["amounts"][0]["address"], myzaddr) assert_equal(params["amounts"][0]["address"], myzaddr)
assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789')) assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))
@ -198,7 +198,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
unshieldvalue = Decimal('10.0') unshieldvalue = Decimal('10.0')
recipients = [] recipients = []
recipients.append({"address":mytaddr, "amount": unshieldvalue}) 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) mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
assert(mytxid is not None) assert(mytxid is not None)
self.sync_all() self.sync_all()
@ -224,7 +224,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
recipients = [] recipients = []
amount = Decimal('10.0') - DEFAULT_FEE - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold 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 }) 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)") 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 # 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 # z_sendmany will fail because of insufficient funds
recipients = [] recipients = []
recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000.0')}) 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.") 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.") 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 # Send will fail because of insufficient funds unless sender uses coinbase utxos
@ -276,7 +276,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
self.nodes[0].getinfo() self.nodes[0].getinfo()
# Issue #2263 Workaround END # Issue #2263 Workaround END
myopid = self.nodes[0].z_sendmany(myzaddr, recipients) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1)
try: try:
wait_and_assert_operationid_status(self.nodes[0], myopid) wait_and_assert_operationid_status(self.nodes[0], myopid)
except JSONRPCException as e: except JSONRPCException as e:

View File

@ -75,7 +75,7 @@ class WalletTreeStateTest (BitcoinTestFramework):
recipients = [] recipients = []
recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('18.0')}) 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}) 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... # Wait for Tx 2 to begin executing...
for x in range(1, 60): for x in range(1, 60):

View File

@ -23,7 +23,7 @@ from decimal import Decimal
class WalletZSendmanyTest(BitcoinTestFramework): class WalletZSendmanyTest(BitcoinTestFramework):
def setup_network(self, split=False): def setup_network(self, split=False):
self.nodes = start_nodes(3, self.options.tmpdir, [[ self.nodes = start_nodes(3, self.options.tmpdir, [[
nuparams(NU5_BRANCH_ID, 220), nuparams(NU5_BRANCH_ID, 240),
]] * self.num_nodes) ]] * self.num_nodes)
connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,1,2)
@ -91,7 +91,7 @@ class WalletZSendmanyTest(BitcoinTestFramework):
# send from node 0 to node 2 taddr # send from node 0 to node 2 taddr
mytxid = self.nodes[0].sendtoaddress(mytaddr, 10.0) mytxid = self.nodes[0].sendtoaddress(mytaddr, 10.0)
self.sync_all() self.sync_all()
self.nodes[0].generate(1) self.nodes[0].generate(10)
self.sync_all() self.sync_all()
# send node 2 taddr to zaddr # 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_unconfirmed_balance"]), zsendmanynotevalue)
assert_equal(Decimal(wallet_info["shielded_balance"]), Decimal('0.0')) assert_equal(Decimal(wallet_info["shielded_balance"]), Decimal('0.0'))
self.nodes[2].generate(1) self.nodes[2].generate(10)
self.sync_all() self.sync_all()
assert_equal(self.nodes[2].getbalance(), node2utxobalance) 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( OrchardSpendInfoPtr* orchard_wallet_get_spend_info(
const OrchardWalletPtr* wallet, const OrchardWalletPtr* wallet,
const unsigned char txid[32], 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 * Run the garbage collection operation on the wallet's note commitment

View File

@ -1,22 +1,26 @@
use byteorder::{ReadBytesExt, WriteBytesExt}; use byteorder::{ReadBytesExt, WriteBytesExt};
use std::collections::{BTreeMap, BTreeSet};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use incrementalmerkletree::{ use incrementalmerkletree::{
bridgetree::{BridgeTree, Checkpoint}, bridgetree::{BridgeTree, Checkpoint},
Hashable, Hashable, Position,
}; };
use zcash_encoding::{Optional, Vector}; use zcash_encoding::{Optional, Vector};
use zcash_primitives::merkle_tree::{ use zcash_primitives::merkle_tree::{
incremental::{ incremental::{
read_bridge_v1, read_leu64_usize, read_position, write_bridge_v1, write_position, 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, 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())?; write_usize_leu64(&mut writer, checkpoint.bridges_len())?;
writer.write_u8(if checkpoint.is_witnessed() { 1 } else { 0 })?; 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( Vector::write_sized(
&mut writer, &mut writer,
checkpoint.forgotten().iter(), checkpoint.forgotten().iter(),
@ -29,25 +33,54 @@ pub fn write_checkpoint_v1<W: Write>(mut writer: W, checkpoint: &Checkpoint) ->
Ok(()) 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( Ok(Checkpoint::from_parts(
read_leu64_usize(&mut reader)?, read_leu64_usize(&mut reader)?,
reader.read_u8()? == 1, reader.read_u8()? == 1,
Vector::read_collected(&mut reader, |r| read_position(r))?,
Vector::read_collected(&mut reader, |mut r| { Vector::read_collected(&mut reader, |mut r| {
Ok((read_position(&mut r)?, read_leu64_usize(&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, mut writer: W,
tree: &BridgeTree<H, 32>, tree: &BridgeTree<H, 32>,
) -> io::Result<()> { ) -> io::Result<()> {
Vector::write(&mut writer, tree.prior_bridges(), |w, b| { writer.write_u8(SER_V2)?;
write_bridge_v1(w, b) 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| { Optional::write(&mut writer, tree.current_bridge().as_ref(), |mut w, b| {
write_bridge_v1(w, b) write_bridge_v1(&mut w, b)
})?; })?;
Vector::write_sized( Vector::write_sized(
&mut writer, &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| { 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())?; 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)] #[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, mut reader: R,
) -> io::Result<BridgeTree<H, 32>> { ) -> 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( BridgeTree::from_parts(
Vector::read(&mut reader, |r| read_bridge_v1(r))?, prior_bridges,
Optional::read(&mut reader, |r| read_bridge_v1(r))?, current_bridge,
Vector::read_collected(&mut reader, |mut r| { saved,
Ok((read_position(&mut r)?, read_leu64_usize(&mut r)?)) checkpoints,
})?, max_checkpoints,
Vector::read(&mut reader, |r| read_checkpoint_v1(r))?,
read_leu64_usize(&mut reader)?,
) )
.map_err(|err| { .map_err(|err| {
io::Error::new( 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 byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use incrementalmerkletree::{bridgetree, bridgetree::BridgeTree, Frontier, Position, Tree}; use incrementalmerkletree::{bridgetree, bridgetree::BridgeTree, Position, Tree};
use libc::c_uchar; use libc::c_uchar;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryInto; use std::convert::TryInto;
@ -201,6 +201,8 @@ pub enum SpendRetrievalError {
NoIvkForRecipient(Address), NoIvkForRecipient(Address),
FvkNotFound(IncomingViewingKey), FvkNotFound(IncomingViewingKey),
NoteNotPositioned(OutPoint), NoteNotPositioned(OutPoint),
InvalidMerkleRoot,
WitnessNotAvailableAtRoot(MerkleHashOrchard),
} }
/// A struct used to return metadata about how a bundle was determined /// A struct used to return metadata about how a bundle was determined
@ -686,12 +688,12 @@ impl Wallet {
.collect() .collect()
} }
pub fn note_commitment_tree_root(&self) -> MerkleHashOrchard { pub fn note_commitment_tree_root(&self, checkpoint_depth: usize) -> Option<MerkleHashOrchard> {
self.witness_tree.root() self.witness_tree.root(checkpoint_depth)
} }
/// Fetches the information necessary to spend the note at the given `OutPoint`, /// 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 /// 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 has not been passed to `Wallet::append_bundle_commitments`.
@ -699,6 +701,7 @@ impl Wallet {
pub fn get_spend_info( pub fn get_spend_info(
&self, &self,
outpoint: OutPoint, outpoint: OutPoint,
as_of_root: MerkleHashOrchard,
) -> Result<OrchardSpendInfo, SpendRetrievalError> { ) -> Result<OrchardSpendInfo, SpendRetrievalError> {
// TODO: Take `confirmations` parameter and obtain the Merkle path to the root at // TODO: Take `confirmations` parameter and obtain the Merkle path to the root at
// that checkpoint, not the latest root. // that checkpoint, not the latest root.
@ -738,8 +741,8 @@ impl Wallet {
let path = self let path = self
.witness_tree .witness_tree
.authentication_path(*position) .authentication_path(*position, &as_of_root)
.expect("wallet always has paths to positioned notes"); .ok_or(SpendRetrievalError::WitnessNotAvailableAtRoot(as_of_root))?;
Ok(OrchardSpendInfo::from_parts( Ok(OrchardSpendInfo::from_parts(
fvk.clone(), 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 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."); 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] #[no_mangle]
@ -1213,13 +1218,23 @@ pub extern "C" fn orchard_wallet_get_spend_info(
wallet: *const Wallet, wallet: *const Wallet,
txid: *const [c_uchar; 32], txid: *const [c_uchar; 32],
action_idx: usize, action_idx: usize,
as_of_root: *const [c_uchar; 32],
) -> *mut OrchardSpendInfo { ) -> *mut OrchardSpendInfo {
let wallet = unsafe { wallet.as_ref() }.expect("Wallet pointer may not be null."); 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 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 }; 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)), Ok(ret) => Box::into_raw(Box::new(ret)),
Err(e) => { Err(e) => {
tracing::error!( tracing::error!(

View File

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

View File

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

View File

@ -497,7 +497,9 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
{ {
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, witnesses, anchor); 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 // 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 // If we attempt to get spend info now, it will fail because the note hasn't
// been witnessed in the Orchard commitment tree. // 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. // Append the bundle to the wallet's commitment tree.
CBlock fakeBlock; CBlock fakeBlock;
@ -119,7 +119,7 @@ TEST(TransactionBuilder, OrchardToOrchard) {
ASSERT_TRUE(wallet.AppendNoteCommitments(2, fakeBlock)); ASSERT_TRUE(wallet.AppendNoteCommitments(2, fakeBlock));
// Now we can get spend info for the note. // 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); EXPECT_EQ(spendInfo[0].second.Value(), 40000);
// Get the root of the commitment tree. // Get the root of the commitment tree.

View File

@ -884,8 +884,8 @@ TEST(WalletTests, GetConflictedOrchardNotes) {
auto recipient2 = ivk.Address(j2); auto recipient2 = ivk.Address(j2);
// Generate tx to spend note A // Generate tx to spend note A
auto noteToSpend = std::move(wallet.GetOrchardSpendInfo(orchardEntries)[0]);
auto builder2 = TransactionBuilder(consensusParams, 2, orchardTree.root()); 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)); builder2.AddOrchardSpend(std::move(noteToSpend.first), std::move(noteToSpend.second));
auto maybeTx2 = builder2.Build(); auto maybeTx2 = builder2.Build();
EXPECT_TRUE(maybeTx2.IsTx()); EXPECT_TRUE(maybeTx2.IsTx());
@ -897,7 +897,7 @@ TEST(WalletTests, GetConflictedOrchardNotes) {
CWalletTx wtx2 {&wallet, tx2}; CWalletTx wtx2 {&wallet, tx2};
// Generate conflicting tx to spend note A // 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()); auto builder3 = TransactionBuilder(consensusParams, 2, orchardTree.root());
builder3.AddOrchardSpend(std::move(noteToSpend2.first), std::move(noteToSpend2.second)); builder3.AddOrchardSpend(std::move(noteToSpend2.first), std::move(noteToSpend2.second));
auto maybeTx3 = builder3.Build(); 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( 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; std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> result;
for (const auto& note : noteMetadata) { for (const auto& note : noteMetadata) {
auto pSpendInfo = orchard_wallet_get_spend_info( auto pSpendInfo = orchard_wallet_get_spend_info(
inner.get(), inner.get(),
note.GetOutPoint().hash.begin(), note.GetOutPoint().hash.begin(),
note.GetOutPoint().n); note.GetOutPoint().n,
anchor.begin());
if (pSpendInfo == nullptr) { if (pSpendInfo == nullptr) {
throw std::logic_error("Called OrchardWallet::GetSpendInfo with unknown outpoint"); throw std::logic_error("Called OrchardWallet::GetSpendInfo with unknown outpoint");
} else { } else {

View File

@ -442,7 +442,8 @@ public:
} }
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> GetSpendInfo( 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() { void GarbageCollect() {
orchard_wallet_gc_note_commitment_tree(inner.get()); orchard_wallet_gc_note_commitment_tree(inner.get());

View File

@ -47,6 +47,7 @@
#include <univalue.h> #include <univalue.h>
#include <algorithm>
#include <numeric> #include <numeric>
#include <optional> #include <optional>
#include <variant> #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" " \"address\":amount (numeric) The Zcash address is the key, the numeric amount in " + CURRENCY_UNIT + " is the value\n"
" ,...\n" " ,...\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" "4. \"comment\" (string, optional) A comment\n"
"5. subtractfeefromamount (string, optional) A json array with addresses.\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" " 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) if (fHelp || params.size() < 2 || params.size() > 5)
throw runtime_error( throw runtime_error(
strprintf(
"z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee ) ( privacyPolicy )\n" "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee ) ( privacyPolicy )\n"
"\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision." "\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" "\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" " \"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" " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n"
" }, ... ]\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" "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" "5. privacyPolicy (string, optional, default=\"LegacyCompat\") Policy for what information leakage is acceptable.\n"
" One of the following strings:\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", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]'")
+ HelpExampleCli("z_sendmany", "\"ANY_TADDR\" '[{\"address\": \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"amount\": 2.0}]'") + HelpExampleCli("z_sendmany", "\"ANY_TADDR\" '[{\"address\": \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"amount\": 2.0}]'")
+ HelpExampleRpc("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]") + HelpExampleRpc("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]")
, nOrchardAnchorConfirmations)
); );
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
@ -5089,7 +5092,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
} }
// Minimum confirmations // Minimum confirmations
int nMinDepth = 1; int nMinDepth = nOrchardAnchorConfirmations;
if (params.size() > 2) { if (params.size() > 2) {
nMinDepth = params[2].get_int(); nMinDepth = params[2].get_int();
} }
@ -5134,11 +5137,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
UniValue contextInfo = o; UniValue contextInfo = o;
std::optional<uint256> orchardAnchor; std::optional<uint256> orchardAnchor;
if (!ztxoSelector.SelectsSprout()) { if (!ztxoSelector.SelectsSprout() && nMinDepth > 0) {
// Allow Orchard recipients by setting an Orchard anchor. // Allow Orchard recipients by setting an Orchard anchor.
// TODO: Add an orchardAnchorHeight field to ZTXOSelector so we can ensure the // TODO: Add an orchardAnchorHeight field to ZTXOSelector so we can ensure the
// same anchor is used for witnesses of any selected Orchard note. // 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; orchardAnchor = chainActive[orchardAnchorHeight]->hashFinalOrchardRoot;
} }
TransactionBuilder builder(chainparams.GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain); 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( 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); AssertLockHeld(cs_wallet);
return orchardWallet.GetSpendInfo(orchardNoteMetadata); return orchardWallet.GetSpendInfo(orchardNoteMetadata, anchor);
} }
isminetype CWallet::IsMine(const CTxIn &txin) const 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. //! Amount of entropy used in generation of the mnemonic seed, in bytes.
static const size_t WALLET_MNEMONIC_ENTROPY_LENGTH = 32; static const size_t WALLET_MNEMONIC_ENTROPY_LENGTH = 32;
//! -orchardanchorconfirmations default //! -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; extern const char * DEFAULT_WALLET_DAT;
@ -1787,7 +1787,8 @@ public:
std::vector<std::optional<SaplingWitness>>& witnesses, std::vector<std::optional<SaplingWitness>>& witnesses,
uint256 &final_anchor); uint256 &final_anchor);
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> GetOrchardSpendInfo( 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; isminetype IsMine(const CTxIn& txin) const;
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;