TransactionSaplingValid

This commit is contained in:
Svyatoslav Nikolsky 2018-11-27 11:09:11 +03:00
parent 964f027422
commit 3a1cf6dc15
9 changed files with 516 additions and 47 deletions

99
Cargo.lock generated
View File

@ -65,6 +65,21 @@ dependencies = [
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "bellman"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "bencher" name = "bencher"
version = "0.1.0" version = "0.1.0"
@ -131,6 +146,16 @@ dependencies = [
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "blake2-rfc"
version = "0.2.18"
source = "git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9#7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
dependencies = [
"arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.2.3" version = "1.2.3"
@ -187,6 +212,11 @@ name = "constant_time_eq"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crossbeam"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.2.0" version = "0.2.0"
@ -204,7 +234,7 @@ dependencies = [
"arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -253,6 +283,14 @@ dependencies = [
"test-data 0.1.0", "test-data 0.1.0",
] ]
[[package]]
name = "digest"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "display_derive" name = "display_derive"
version = "0.0.0" version = "0.0.0"
@ -373,6 +411,14 @@ dependencies = [
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "generic-array"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.15" version = "0.2.15"
@ -540,7 +586,7 @@ dependencies = [
"base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitcrypto 0.1.0", "bitcrypto 0.1.0",
"eth-secp256k1 0.5.7 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.7 (git+https://github.com/ethcore/rust-secp256k1)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -553,7 +599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.0.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -732,7 +778,7 @@ name = "network"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chain 0.1.0", "chain 0.1.0",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serialization 0.1.0", "serialization 0.1.0",
@ -806,6 +852,15 @@ dependencies = [
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "pairing"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.4.8" version = "0.4.8"
@ -929,7 +984,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1075,6 +1130,19 @@ name = "safemem"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sapling-crypto"
version = "0.0.1"
source = "git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e#21084bde2019c04bd34208e63c3560fe2c02fb0e"
dependencies = [
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "scoped-tls" name = "scoped-tls"
version = "0.1.0" version = "0.1.0"
@ -1340,7 +1408,7 @@ name = "thread_local"
version = "0.3.5" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1405,6 +1473,11 @@ dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "typenum"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "ucd-util" name = "ucd-util"
version = "0.1.2" version = "0.1.2"
@ -1450,18 +1523,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "verification" name = "verification"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitcrypto 0.1.0", "bitcrypto 0.1.0",
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)", "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"chain 0.1.0", "chain 0.1.0",
"db 0.1.0", "db 0.1.0",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"network 0.1.0", "network 0.1.0",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)",
"script 0.1.0", "script 0.1.0",
"serialization 0.1.0", "serialization 0.1.0",
"storage 0.1.0", "storage 0.1.0",
@ -1553,23 +1630,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860"
"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" "checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
"checksum bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eae372472c7ea8f7c8fc6a62f7d5535db8302de7f1aafda2e13a97c4830d3bcf"
"checksum bigint 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5d1b3ef6756498df0e2c6bb67c065f4154d0ecd721eb5b3c3f865c8012b9fd74" "checksum bigint 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5d1b3ef6756498df0e2c6bb67c065f4154d0ecd721eb5b3c3f865c8012b9fd74"
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)" = "<none>" "checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)" = "<none>"
"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "<none>"
"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9"
"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" "checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
"checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum csv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71903184af9960c555e7f3b32ff17390d20ecaaf17d4f18c4a0993f2df8a49e3" "checksum csv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71903184af9960c555e7f3b32ff17390d20ecaaf17d4f18c4a0993f2df8a49e3"
"checksum csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dd8e6d86f7ba48b4276ef1317edc8cc36167546d8972feb4a2b5fec0b374105" "checksum csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dd8e6d86f7ba48b4276ef1317edc8cc36167546d8972feb4a2b5fec0b374105"
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
"checksum display_derive 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bba5dcd6d2855639fcf65a9af7bbad0bfb6dbf6fe68fba70bab39a6eb973ef4" "checksum display_derive 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bba5dcd6d2855639fcf65a9af7bbad0bfb6dbf6fe68fba70bab39a6eb973ef4"
"checksum domain 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1850bf2c3c3349e1dba2aa214d86cf9edaa057a09ce46b1a02d5c07d5da5e65" "checksum domain 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1850bf2c3c3349e1dba2aa214d86cf9edaa057a09ce46b1a02d5c07d5da5e65"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
@ -1585,6 +1666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" "checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1"
"checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf" "checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf"
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9" "checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
"checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a" "checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a"
"checksum heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "556cd479866cf85c3f671209c85e8a6990211c916d1002c2fcb2e9b7cf60bc36" "checksum heapsize 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "556cd479866cf85c3f671209c85e8a6990211c916d1002c2fcb2e9b7cf60bc36"
@ -1601,7 +1683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum jsonrpc-server-utils 8.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>" "checksum jsonrpc-server-utils 8.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" "checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2"
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
@ -1624,6 +1706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595" "checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
@ -1649,6 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" "checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69"
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
"checksum sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)" = "<none>"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
@ -1680,6 +1764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743" "checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum ucd-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f8bfa9ff0cadcd210129ad9d2c5f145c13e9ced3d3e5d948a6213487d52444" "checksum ucd-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f8bfa9ff0cadcd210129ad9d2c5f145c13e9ced3d3e5d948a6213487d52444"
"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"

View File

@ -1,38 +1,81 @@
use std::fmt; use std::fmt;
use hex::ToHex; use hex::ToHex;
///
#[derive(Clone)] #[derive(Clone)]
pub struct Sapling { pub struct Sapling {
pub amount: i64, /// The net value of Spend transfers minus Output transfers in a transaction is
/// called the balancing_value.
/// A positive balancing_value takes value from the Sapling value pool and adds
/// it to the transparent value pool.
/// A negative balancing value does the reverse.
pub balancing_value: i64,
/// A Spend transfer spends a note. Its Spend description includes a Pedersen value
/// commitment to the value of the note. It is associated with an instance of a Spend
/// statement for which it provides a zk-SNARK proof.
pub spends: Vec<SaplingSpendDescription>, pub spends: Vec<SaplingSpendDescription>,
/// An Output transfer creates a note. Its Output description includes a Pedersen value
/// commitment to the note value. It is associated with an instance of an Output statement
/// for which it provides a zk-SNARK proof.
pub outputs: Vec<SaplingOutputDescription>, pub outputs: Vec<SaplingOutputDescription>,
/// Consistency of balancing_value with the value commitments in Spend descriptions
/// and Output descriptions is enforced by the binding_sig.
/// This signature has a dual role in Sapling:
/// 1) to prove that the total value spent by Spend transfers, minus that produced by
/// Output transfers, is consistent with the v balance field of the transaction;
/// 2) To prove that the signer knew the randomness used for the spend and output value
/// commitments, in order to prevent Output descriptions from being replayed by an
/// adversary in a different transaction. (A Spend description already cannot be
/// replayed due to its spend authorization signature.)
pub binding_sig: [u8; 64], pub binding_sig: [u8; 64],
} }
/// Single Spend transfer description.
#[derive(Clone, Serializable, Deserializable)] #[derive(Clone, Serializable, Deserializable)]
pub struct SaplingSpendDescription { pub struct SaplingSpendDescription {
pub cv: [u8; 32], /// Value commitment to the value of the input note.
pub value_commitment: [u8; 32],
/// An anchor for the output treestate of a previous block.
pub anchor: [u8; 32], pub anchor: [u8; 32],
/// The nullifier for the input note.
pub nullifier: [u8; 32], pub nullifier: [u8; 32],
pub rk: [u8; 32], /// Randomized public key that should be used to verify spend_auth_sig.
pub randomized_key: [u8; 32],
/// Zero-knowledge proof with primary input
/// (value_commitment, anchor, nullifier, randomized_key)
/// for the spend statement.
pub zkproof: [u8; 192], pub zkproof: [u8; 192],
/// Spend authorization signature. Is used to prove knowledge of the spending key
/// authorizing spending of an input note.
pub spend_auth_sig: [u8; 64], pub spend_auth_sig: [u8; 64],
} }
/// Single Output transfer description.
#[derive(Clone, Serializable, Deserializable)] #[derive(Clone, Serializable, Deserializable)]
pub struct SaplingOutputDescription { pub struct SaplingOutputDescription {
pub cv: [u8; 32], /// Value commitment to the value of the output note.
pub cm: [u8; 32], pub value_commitment: [u8; 32],
/// The note commitment for the output note.
pub note_commitment: [u8; 32],
/// Key agreement public key, used to derive the key for encryption of the transmitted
/// note ciphertext.
pub ephemeral_key: [u8; 32], pub ephemeral_key: [u8; 32],
/// Ciphertext component for the encrypted output note.
pub enc_cipher_text: [u8; 580], pub enc_cipher_text: [u8; 580],
/// Ciphertext component that allows the holder of a full viewing key to recover the recipient
/// diversified transmission key and teh ephemeral private key (and therefore the entire note
/// plaintext).
pub out_cipher_text: [u8; 80], pub out_cipher_text: [u8; 80],
/// Zero-knowledge proof with primary input
/// (value_commitment, cm, ephemeral_key)
/// for the output statement.
pub zkproof: [u8; 192], pub zkproof: [u8; 192],
} }
impl Default for Sapling { impl Default for Sapling {
fn default() -> Self { fn default() -> Self {
Sapling { Sapling {
amount: Default::default(), balancing_value: Default::default(),
spends: Default::default(), spends: Default::default(),
outputs: Default::default(), outputs: Default::default(),
binding_sig: [0; 64], binding_sig: [0; 64],
@ -43,7 +86,7 @@ impl Default for Sapling {
impl fmt::Debug for Sapling { impl fmt::Debug for Sapling {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Sapling") f.debug_struct("Sapling")
.field("amount", &self.amount) .field("balancing_value", &self.balancing_value)
.field("spends", &self.spends) .field("spends", &self.spends)
.field("outputs", &self.outputs) .field("outputs", &self.outputs)
.field("binding_sig", &self.binding_sig.to_hex::<String>()) .field("binding_sig", &self.binding_sig.to_hex::<String>())
@ -53,7 +96,7 @@ impl fmt::Debug for Sapling {
impl PartialEq<Sapling> for Sapling { impl PartialEq<Sapling> for Sapling {
fn eq(&self, other: &Sapling) -> bool { fn eq(&self, other: &Sapling) -> bool {
self.amount == other.amount self.balancing_value == other.balancing_value
&& self.spends == other.spends && self.spends == other.spends
&& self.outputs == other.outputs && self.outputs == other.outputs
&& self.binding_sig.as_ref() == other.binding_sig.as_ref() && self.binding_sig.as_ref() == other.binding_sig.as_ref()
@ -63,10 +106,10 @@ impl PartialEq<Sapling> for Sapling {
impl Default for SaplingSpendDescription { impl Default for SaplingSpendDescription {
fn default() -> Self { fn default() -> Self {
SaplingSpendDescription { SaplingSpendDescription {
cv: Default::default(), value_commitment: Default::default(),
anchor: Default::default(), anchor: Default::default(),
nullifier: Default::default(), nullifier: Default::default(),
rk: Default::default(), randomized_key: Default::default(),
zkproof: [0; 192], zkproof: [0; 192],
spend_auth_sig: [0; 64], spend_auth_sig: [0; 64],
} }
@ -76,10 +119,10 @@ impl Default for SaplingSpendDescription {
impl fmt::Debug for SaplingSpendDescription { impl fmt::Debug for SaplingSpendDescription {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SaplingSpendDescription") f.debug_struct("SaplingSpendDescription")
.field("cv", &self.cv.to_hex::<String>()) .field("value_commitment", &self.value_commitment.to_hex::<String>())
.field("anchor", &self.anchor.to_hex::<String>()) .field("anchor", &self.anchor.to_hex::<String>())
.field("nullifier", &self.nullifier.to_hex::<String>()) .field("nullifier", &self.nullifier.to_hex::<String>())
.field("rk", &self.rk.to_hex::<String>()) .field("randomized_key", &self.randomized_key.to_hex::<String>())
.field("zkproof", &self.zkproof.to_hex::<String>()) .field("zkproof", &self.zkproof.to_hex::<String>())
.field("spend_auth_sig", &self.spend_auth_sig.to_hex::<String>()) .field("spend_auth_sig", &self.spend_auth_sig.to_hex::<String>())
.finish() .finish()
@ -88,10 +131,10 @@ impl fmt::Debug for SaplingSpendDescription {
impl PartialEq<SaplingSpendDescription> for SaplingSpendDescription { impl PartialEq<SaplingSpendDescription> for SaplingSpendDescription {
fn eq(&self, other: &SaplingSpendDescription) -> bool { fn eq(&self, other: &SaplingSpendDescription) -> bool {
self.cv == other.cv self.value_commitment == other.value_commitment
&& self.anchor == other.anchor && self.anchor == other.anchor
&& self.nullifier == other.nullifier && self.nullifier == other.nullifier
&& self.rk == other.rk && self.randomized_key == other.randomized_key
&& self.zkproof.as_ref() == other.zkproof.as_ref() && self.zkproof.as_ref() == other.zkproof.as_ref()
&& self.spend_auth_sig.as_ref() == other.spend_auth_sig.as_ref() && self.spend_auth_sig.as_ref() == other.spend_auth_sig.as_ref()
} }
@ -100,8 +143,8 @@ impl PartialEq<SaplingSpendDescription> for SaplingSpendDescription {
impl Default for SaplingOutputDescription { impl Default for SaplingOutputDescription {
fn default() -> Self { fn default() -> Self {
SaplingOutputDescription { SaplingOutputDescription {
cv: Default::default(), value_commitment: Default::default(),
cm: Default::default(), note_commitment: Default::default(),
ephemeral_key: Default::default(), ephemeral_key: Default::default(),
enc_cipher_text: [0; 580], enc_cipher_text: [0; 580],
out_cipher_text: [0; 80], out_cipher_text: [0; 80],
@ -113,8 +156,8 @@ impl Default for SaplingOutputDescription {
impl fmt::Debug for SaplingOutputDescription { impl fmt::Debug for SaplingOutputDescription {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SaplingSpendDescription") f.debug_struct("SaplingSpendDescription")
.field("cv", &self.cv.to_hex::<String>()) .field("value_commitment", &self.value_commitment.to_hex::<String>())
.field("cm", &self.cm.to_hex::<String>()) .field("note_commitment", &self.note_commitment.to_hex::<String>())
.field("ephemeral_key", &self.ephemeral_key.to_hex::<String>()) .field("ephemeral_key", &self.ephemeral_key.to_hex::<String>())
.field("enc_cipher_text", &self.enc_cipher_text.to_hex::<String>()) .field("enc_cipher_text", &self.enc_cipher_text.to_hex::<String>())
.field("out_cipher_text", &self.out_cipher_text.to_hex::<String>()) .field("out_cipher_text", &self.out_cipher_text.to_hex::<String>())
@ -125,8 +168,8 @@ impl fmt::Debug for SaplingOutputDescription {
impl PartialEq<SaplingOutputDescription> for SaplingOutputDescription { impl PartialEq<SaplingOutputDescription> for SaplingOutputDescription {
fn eq(&self, other: &SaplingOutputDescription) -> bool { fn eq(&self, other: &SaplingOutputDescription) -> bool {
self.cv == other.cv self.value_commitment == other.value_commitment
&& self.cm == other.cm && self.note_commitment == other.note_commitment
&& self.ephemeral_key == other.ephemeral_key && self.ephemeral_key == other.ephemeral_key
&& self.enc_cipher_text.as_ref() == other.enc_cipher_text.as_ref() && self.enc_cipher_text.as_ref() == other.enc_cipher_text.as_ref()
&& self.out_cipher_text.as_ref() == other.out_cipher_text.as_ref() && self.out_cipher_text.as_ref() == other.out_cipher_text.as_ref()

View File

@ -224,7 +224,7 @@ impl Serializable for Transaction {
} }
if let Some(sapling) = self.sapling.as_ref() { if let Some(sapling) = self.sapling.as_ref() {
stream.append(&sapling.amount) stream.append(&sapling.balancing_value)
.append_list(&sapling.spends) .append_list(&sapling.spends)
.append_list(&sapling.outputs); .append_list(&sapling.outputs);
} }
@ -282,11 +282,11 @@ impl Deserializable for Transaction {
}; };
let mut sapling = if is_sapling_tx { let mut sapling = if is_sapling_tx {
let amount = reader.read()?; let balancing_value = reader.read()?;
let spends = reader.read_list()?; let spends = reader.read_list()?;
let outputs = reader.read_list()?; let outputs = reader.read_list()?;
Some(Sapling { Some(Sapling {
amount, balancing_value,
spends, spends,
outputs, outputs,
..Default::default() ..Default::default()

View File

@ -8,8 +8,12 @@ time = "0.1"
log = "0.4" log = "0.4"
rayon = "1.0" rayon = "1.0"
parking_lot = "0.4" parking_lot = "0.4"
bellman = "0.1"
blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", branch = "persona" } blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", branch = "persona" }
byteorder = "1.2" byteorder = "1.2"
lazy_static = "1.2.0"
pairing = "0.14.2"
sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" }
primitives = { path = "../primitives" } primitives = { path = "../primitives" }
chain = { path = "../chain" } chain = { path = "../chain" }
serialization = { path = "../serialization" } serialization = { path = "../serialization" }

View File

@ -4,6 +4,7 @@ use network::{ConsensusParams};
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner}; use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
use duplex_store::DuplexTransactionOutputProvider; use duplex_store::DuplexTransactionOutputProvider;
use deployments::BlockDeployments; use deployments::BlockDeployments;
use sapling::accept_sapling;
use sigops::transaction_sigops; use sigops::transaction_sigops;
use canon::CanonTransaction; use canon::CanonTransaction;
use constants::{COINBASE_MATURITY}; use constants::{COINBASE_MATURITY};
@ -15,6 +16,7 @@ pub struct TransactionAcceptor<'a> {
pub bip30: TransactionBip30<'a>, pub bip30: TransactionBip30<'a>,
pub missing_inputs: TransactionMissingInputs<'a>, pub missing_inputs: TransactionMissingInputs<'a>,
pub maturity: TransactionMaturity<'a>, pub maturity: TransactionMaturity<'a>,
pub sapling_valid: TransactionSaplingValid<'a>,
pub overspent: TransactionOverspent<'a>, pub overspent: TransactionOverspent<'a>,
pub double_spent: TransactionDoubleSpend<'a>, pub double_spent: TransactionDoubleSpend<'a>,
pub eval: TransactionEval<'a>, pub eval: TransactionEval<'a>,
@ -41,6 +43,7 @@ impl<'a> TransactionAcceptor<'a> {
bip30: TransactionBip30::new_for_sync(transaction, meta_store), bip30: TransactionBip30::new_for_sync(transaction, meta_store),
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index), missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
maturity: TransactionMaturity::new(transaction, meta_store, height), maturity: TransactionMaturity::new(transaction, meta_store, height),
sapling_valid: TransactionSaplingValid::new(transaction),
overspent: TransactionOverspent::new(transaction, output_store), overspent: TransactionOverspent::new(transaction, output_store),
double_spent: TransactionDoubleSpend::new(transaction, output_store), double_spent: TransactionDoubleSpend::new(transaction, output_store),
eval: TransactionEval::new(transaction, output_store, consensus, verification_level, height, time, deployments), eval: TransactionEval::new(transaction, output_store, consensus, verification_level, height, time, deployments),
@ -52,6 +55,7 @@ impl<'a> TransactionAcceptor<'a> {
self.bip30.check()?; self.bip30.check()?;
self.missing_inputs.check()?; self.missing_inputs.check()?;
self.maturity.check()?; self.maturity.check()?;
self.sapling_valid.check()?;
self.overspent.check()?; self.overspent.check()?;
self.double_spent.check()?; self.double_spent.check()?;
self.eval.check()?; self.eval.check()?;
@ -432,6 +436,29 @@ impl<'a> TransactionSize<'a> {
} }
} }
/// Checks that sapling signatures/proofs are valid.
pub struct TransactionSaplingValid<'a> {
transaction: CanonTransaction<'a>,
}
impl<'a> TransactionSaplingValid<'a> {
fn new(transaction: CanonTransaction<'a>) -> Self {
TransactionSaplingValid {
transaction: transaction,
}
}
fn check(&self) -> Result<(), TransactionError> {
if let Some(sapling) = self.transaction.raw.sapling.as_ref() {
let sighash = Default::default(); // TODO: pass real sighash when ready
accept_sapling(&sighash, sapling)
.map_err(|_| TransactionError::InvalidSapling)?;
}
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -465,4 +492,4 @@ mod tests {
.verify_p2sh(true); .verify_p2sh(true);
assert_eq!(verify_script(&input_script, &output_script, &flags, &checker), Ok(())); assert_eq!(verify_script(&input_script, &output_script, &flags, &checker), Ok(()));
} }
} }

View File

@ -129,5 +129,7 @@ pub enum TransactionError {
DuplicateJoinSplitNullifier(usize, usize), DuplicateJoinSplitNullifier(usize, usize),
/// Transaction has sapling spends with duplicate nullifiers. Sapling spends indexes are provided. /// Transaction has sapling spends with duplicate nullifiers. Sapling spends indexes are provided.
DuplicateSaplingSpendNullifier(usize, usize), DuplicateSaplingSpendNullifier(usize, usize),
/// Transaction sapling verification has failed.
InvalidSapling,
} }

View File

@ -56,11 +56,16 @@ extern crate time;
extern crate log; extern crate log;
extern crate parking_lot; extern crate parking_lot;
extern crate rayon; extern crate rayon;
extern crate bellman;
extern crate blake2_rfc; extern crate blake2_rfc;
extern crate byteorder; extern crate byteorder;
#[macro_use]
extern crate lazy_static;
#[cfg(test)] #[cfg(test)]
extern crate rand; extern crate rand;
extern crate rustc_hex as hex; extern crate rustc_hex as hex;
extern crate pairing;
extern crate sapling_crypto;
extern crate storage; extern crate storage;
extern crate chain; extern crate chain;
@ -78,6 +83,7 @@ mod deployments;
mod duplex_store; mod duplex_store;
mod equihash; mod equihash;
mod error; mod error;
mod sapling;
mod sigops; mod sigops;
mod timestamp; mod timestamp;
mod work; mod work;

302
verification/src/sapling.rs Normal file
View File

@ -0,0 +1,302 @@
use std::io::Error as IoError;
use chain::{Sapling, SaplingSpendDescription, SaplingOutputDescription};
use pairing::{bls12_381::{Bls12, Fr, FrRepr}, PrimeField, PrimeFieldRepr, PrimeFieldDecodingError};
use bellman::{SynthesisError, groth16::{verify_proof, PreparedVerifyingKey, Proof,}};
use sapling_crypto::{circuit::multipack, redjubjub::{self, Signature}};
use sapling_crypto::jubjub::{edwards,fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown};
type Point = edwards::Point<Bls12, Unknown>;
lazy_static! {
static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };
static ref SAPLING_SPEND_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static ref SAPLING_OUTPUT_VK: Option<PreparedVerifyingKey<Bls12>> = None;
}
/// Errors that could occur during sapling verification.
pub enum Error {
/// Spend description verification error.
Spend(usize, SpendError),
/// Output description verification error.
Output(usize, OutputError),
/// Invalid balance value.
InvalidBalanceValue,
/// Error deserializing/verifying binding_sig.
BindingSig(SignatureError),
}
/// Errors that can occur during spend description verification.
pub enum SpendError {
/// Error deserializing value commitment.
ValueCommitment(PointError),
/// Error deserializing anchor.
Anchor(PrimeFieldDecodingError),
/// Error deserializing randomized key.
RandomizedKey(PublicKeyError),
/// Error deserializing/verifying spend_auth_sig.
SpendAuthSig(SignatureError),
/// Error deserializing/verifying zk-proof.
Proof(ProofError),
}
/// Errors that can occur during output description verification.
pub enum OutputError {
/// Error deserializing value commitment.
ValueCommitment(PointError),
/// Error deserializing note commitment.
NoteCommitment(PrimeFieldDecodingError),
/// Error deserializing ephemeral key.
EphemeralKey(PointError),
/// Error deserializing/verifying zk-proof.
Proof(ProofError),
}
/// Errors that can occur during point deserialization.
pub enum PointError {
/// The point is invalid.
Invalid(IoError),
/// The point MUST NOT be small order.
SmallOrder,
}
/// Errors that can occur during public key deserialization.
pub enum PublicKeyError {
/// The public key is invalid.
Invalid(IoError),
/// The point corresponding to the public key MUST NOT be small order.
SmallOrder,
}
/// Error that can occur during signature deserialization/verification.
pub enum SignatureError {
/// The signature is invalid.
Invalid(IoError),
/// The signature verifciation has failed.
Failed,
}
/// Proof verification error.
pub enum ProofError {
/// The proof is invalid.
Invalid(IoError),
/// The error that could occur during circuit synthesis context.
Synthesis(SynthesisError),
/// The proof verification has invalid.
Failed,
}
/// Verify sapling proofs/signatures validity.
pub fn accept_sapling(sighash: &[u8; 32], sapling: &Sapling) -> Result<(), Error> {
// binding verification key is not encoded explicitly in transaction and must be recalculated
let mut total = edwards::Point::zero();
// verify each spend description
for (idx, spend) in sapling.spends.iter().enumerate() {
accept_spend(sighash, &mut total, spend)
.map_err(|err| Error::Spend(idx, err))?;
}
// verify each output description
for (idx, output) in sapling.outputs.iter().enumerate() {
accept_output(&mut total, output)
.map_err(|err| Error::Output(idx, err))?;
}
// check binding signature
accept_sapling_final(sighash, total, sapling)
}
/// Verify sapling spend description.
fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescription) -> Result<(), SpendError> {
// deserialize and check value commitment
let value_commitment = require_non_small_order_point(&spend.value_commitment)
.map_err(SpendError::ValueCommitment)?;
// accumulate the value commitment
*total = total.add(&value_commitment, &JUBJUB);
// deserialize the anchor, which should be an element of Fr
let anchor = Fr::from_repr(read_le(&spend.anchor))
.map_err(SpendError::Anchor)?;
// compute the signature's message for randomized key && spend_auth_sig
let mut data_to_be_signed = [0u8; 64];
data_to_be_signed[..32].copy_from_slice(&spend.randomized_key);
data_to_be_signed[32..].copy_from_slice(sighash);
// deserialize and check randomized key
let randomized_key = redjubjub::PublicKey::<Bls12>::read(&spend.randomized_key[..], &JUBJUB)
.map_err(|err| SpendError::RandomizedKey(PublicKeyError::Invalid(err)))?;
if is_small_order(&randomized_key.0) {
return Err(SpendError::RandomizedKey(PublicKeyError::SmallOrder));
}
// deserialize the signature
let spend_auth_sig = Signature::read(&spend.spend_auth_sig[..])
.map_err(|err| SpendError::SpendAuthSig(SignatureError::Invalid(err)))?;
// verify the spend_auth_sig
if !randomized_key.verify(&data_to_be_signed, &spend_auth_sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB) {
return Err(SpendError::SpendAuthSig(SignatureError::Failed));
}
// Add the nullifier through multiscalar packing
let nullifier = multipack::bytes_to_bits_le(&spend.nullifier);
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
assert_eq!(nullifier.len(), 2);
// construct public input for circuit
let (randomized_key_x, randomized_key_y) = randomized_key.0.into_xy();
let (value_commitment_x, value_commitment_y) = value_commitment.into_xy();
let public_input = [
randomized_key_x,
randomized_key_y,
value_commitment_x,
value_commitment_y,
anchor,
nullifier[0],
nullifier[1],
];
// deserialize the proof
let zkproof = Proof::<Bls12>::read(&spend.zkproof[..])
.map_err(|err| SpendError::Proof(ProofError::Invalid(err)))?;
// check the proof
let verification_key = SAPLING_SPEND_VK.as_ref().expect("TODO");
let is_verification_ok = verify_proof(verification_key, &zkproof, &public_input[..])
.map_err(|err| SpendError::Proof(ProofError::Synthesis(err)))?;
if !is_verification_ok {
return Err(SpendError::Proof(ProofError::Failed));
}
Ok(())
}
fn accept_output(total: &mut Point, output: &SaplingOutputDescription) -> Result<(), OutputError> {
// deserialize and check value commitment
let value_commitment = require_non_small_order_point(&output.value_commitment)
.map_err(OutputError::ValueCommitment)?;
// accumulate the value commitment
*total = total.add(&value_commitment.clone().negate(), &JUBJUB);
// deserialize the commitment, which should be an element of Fr
let note_commitment = Fr::from_repr(read_le(&output.note_commitment))
.map_err(OutputError::NoteCommitment)?;
// deserialize the ephemeral key
let ephemeral_key = require_non_small_order_point(&output.ephemeral_key)
.map_err(OutputError::EphemeralKey)?;
// construct public input for circuit
let (ephemeral_key_x, ephemeral_key_y) = ephemeral_key.into_xy();
let (value_commitment_x, value_commitment_y) = value_commitment.into_xy();
let public_input = [
value_commitment_x,
value_commitment_y,
ephemeral_key_x,
ephemeral_key_y,
note_commitment,
];
// deserialize the proof
let zkproof = Proof::<Bls12>::read(&output.zkproof[..])
.map_err(|err| OutputError::Proof(ProofError::Invalid(err)))?;
// check the proof
let verification_key = SAPLING_OUTPUT_VK.as_ref().expect("TODO");
let is_verification_ok = verify_proof(verification_key, &zkproof, &public_input[..])
.map_err(|err| OutputError::Proof(ProofError::Synthesis(err)))?;
if !is_verification_ok {
return Err(OutputError::Proof(ProofError::Failed));
}
Ok(())
}
fn accept_sapling_final(sighash: &[u8; 32], total: Point, sapling: &Sapling) -> Result<(), Error> {
// obtain current bvk from the context
let mut binding_verification_key = redjubjub::PublicKey(total);
// compute value balance
let mut value_balance = compute_value_balance(sapling.balancing_value)?;
// subtract value_balance from current bvk to get final bvk
value_balance = value_balance.negate();
binding_verification_key.0 = binding_verification_key.0.add(&value_balance, &JUBJUB);
// compute the signature's message for binding_verification_key/binding_sig
let mut data_to_be_signed = [0u8; 64];
binding_verification_key.0.write(&mut data_to_be_signed[..32]).expect("bvk is 32 bytes");
data_to_be_signed[32..].copy_from_slice(&sighash[..]);
// deserialize the binding signature
let binding_sig = Signature::read(&sapling.binding_sig[..])
.map_err(|err| Error::BindingSig(SignatureError::Invalid(err)))?;
// check the binding signature
let is_verification_ok = binding_verification_key
.verify(&data_to_be_signed, &binding_sig, FixedGenerators::ValueCommitmentRandomness, &JUBJUB);
if !is_verification_ok {
return Err(Error::BindingSig(SignatureError::Failed));
}
Ok(())
}
// This function computes `value` in the exponent of the value commitment base
fn compute_value_balance(value: i64) -> Result<Point, Error> {
// Compute the absolute value (failing if -i64::MAX is the value)
let abs = match value.checked_abs() {
Some(a) => a as u64,
None => return Err(Error::InvalidBalanceValue),
};
// Is it negative? We'll have to negate later if so.
let is_negative = value.is_negative();
// Compute it in the exponent
let mut value_balance = JUBJUB
.generator(FixedGenerators::ValueCommitmentValue)
.mul(FsRepr::from(abs), &JUBJUB);
// Negate if necessary
if is_negative {
value_balance = value_balance.negate();
}
// Convert to unknown order point
Ok(value_balance.into())
}
/// Reads an FrRepr from a [u8] of length 32.
/// This will panic (abort) if length provided is
/// not correct.
fn read_le(from: &[u8; 32]) -> FrRepr {
let mut repr = FrRepr::default();
repr.read_le(&from[..]).expect("length is 32 bytes");
repr
}
/// Deserializes point from the serialized buffer, checking that it is on the curve
/// AND it MUST NOT be of small order.
fn require_non_small_order_point(point_buff: &[u8; 32]) -> Result<Point, PointError> {
let point = Point::read(&point_buff[..], &JUBJUB).map_err(PointError::Invalid)?;
if is_small_order(&point) {
return Err(PointError::SmallOrder);
}
Ok(point)
}
/// Is this a small order point?
fn is_small_order(point: &Point) -> bool {
point.double(&JUBJUB).double(&JUBJUB).double(&JUBJUB) == edwards::Point::zero()
}
#[cfg(test)]
mod tests {
// TODO: detailed tests when sighash + verification keys are available
}

View File

@ -356,7 +356,7 @@ impl<'a> TransactionSapling<'a> {
fn check(&self) -> Result<(), TransactionError> { fn check(&self) -> Result<(), TransactionError> {
if let Some(ref sapling) = self.transaction.raw.sapling { if let Some(ref sapling) = self.transaction.raw.sapling {
// sapling balance should be zero if spends and outputs are empty // sapling balance should be zero if spends and outputs are empty
if sapling.amount != 0 && sapling.spends.is_empty() && sapling.outputs.is_empty() { if sapling.balancing_value != 0 && sapling.spends.is_empty() && sapling.outputs.is_empty() {
return Err(TransactionError::EmptySaplingHasBalance); return Err(TransactionError::EmptySaplingHasBalance);
} }
} }
@ -423,13 +423,13 @@ impl<'a> TransactionOutputValueOverflow<'a> {
if let Some(ref sapling) = self.transaction.raw.sapling { if let Some(ref sapling) = self.transaction.raw.sapling {
// check that sapling amount is within limits // check that sapling amount is within limits
if sapling.amount < -self.max_value || sapling.amount > self.max_value { if sapling.balancing_value < -self.max_value || sapling.balancing_value > self.max_value {
return Err(TransactionError::OutputValueOverflow); return Err(TransactionError::OutputValueOverflow);
} }
// negative sapling amount takes value from transparent pool // negative sapling amount takes value from transparent pool
if sapling.amount < 0 { if sapling.balancing_value < 0 {
total_output = match total_output.checked_add(-sapling.amount) { total_output = match total_output.checked_add(-sapling.balancing_value) {
Some(total_output) if total_output <= self.max_value => total_output, Some(total_output) if total_output <= self.max_value => total_output,
_ => return Err(TransactionError::OutputValueOverflow), _ => return Err(TransactionError::OutputValueOverflow),
}; };
@ -493,8 +493,8 @@ impl<'a> TransactionInputValueOverflow<'a> {
if let Some(ref sapling) = self.transaction.raw.sapling { if let Some(ref sapling) = self.transaction.raw.sapling {
// positive sapling amount adds value to the transparent pool // positive sapling amount adds value to the transparent pool
if sapling.amount > 0 { if sapling.balancing_value > 0 {
match total_input.checked_add(sapling.amount as u64) { match total_input.checked_add(sapling.balancing_value as u64) {
Some(total_input) if total_input <= self.max_value => (), Some(total_input) if total_input <= self.max_value => (),
_ => return Err(TransactionError::InputValueOverflow), _ => return Err(TransactionError::InputValueOverflow),
}; };
@ -721,18 +721,18 @@ mod tests {
.into(), &consensus).check(), Ok(())); .into(), &consensus).check(), Ok(()));
assert_eq!(TransactionOutputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionOutputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: max_value, balancing_value: max_value,
..Default::default() ..Default::default()
}).into(), &consensus).check(), Ok(())); }).into(), &consensus).check(), Ok(()));
assert_eq!(TransactionOutputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionOutputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: max_value + 1, balancing_value: max_value + 1,
..Default::default() ..Default::default()
}).into(), &consensus).check(), Err(TransactionError::OutputValueOverflow)); }).into(), &consensus).check(), Err(TransactionError::OutputValueOverflow));
assert_eq!(TransactionOutputValueOverflow::new(&test_data::TransactionBuilder::with_output(max_value as u64 / 2 + 1) assert_eq!(TransactionOutputValueOverflow::new(&test_data::TransactionBuilder::with_output(max_value as u64 / 2 + 1)
.set_sapling(Sapling { .set_sapling(Sapling {
amount: -max_value / 2, balancing_value: -max_value / 2,
..Default::default() ..Default::default()
}).into(), &consensus).check(), Err(TransactionError::OutputValueOverflow)); }).into(), &consensus).check(), Err(TransactionError::OutputValueOverflow));
@ -805,12 +805,12 @@ mod tests {
}).into(), &consensus).check(), Err(TransactionError::InputValueOverflow)); }).into(), &consensus).check(), Err(TransactionError::InputValueOverflow));
assert_eq!(TransactionInputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionInputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: max_value, balancing_value: max_value,
..Default::default() ..Default::default()
}).into(), &consensus).check(), Ok(())); }).into(), &consensus).check(), Ok(()));
assert_eq!(TransactionInputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionInputValueOverflow::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: max_value + 1, balancing_value: max_value + 1,
..Default::default() ..Default::default()
}).into(), &consensus).check(), Err(TransactionError::InputValueOverflow)); }).into(), &consensus).check(), Err(TransactionError::InputValueOverflow));
@ -821,7 +821,7 @@ mod tests {
}], }],
..Default::default() ..Default::default()
}).set_sapling(Sapling { }).set_sapling(Sapling {
amount: max_value / 2, balancing_value: max_value / 2,
..Default::default() ..Default::default()
}).into(), &consensus).check(), Err(TransactionError::InputValueOverflow)); }).into(), &consensus).check(), Err(TransactionError::InputValueOverflow));
} }
@ -842,26 +842,26 @@ mod tests {
#[test] #[test]
fn transaction_sapling_works() { fn transaction_sapling_works() {
assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: 100, balancing_value: 100,
spends: vec![Default::default()], spends: vec![Default::default()],
..Default::default() ..Default::default()
}).into()).check(), Ok(())); }).into()).check(), Ok(()));
assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: 100, balancing_value: 100,
outputs: vec![Default::default()], outputs: vec![Default::default()],
..Default::default() ..Default::default()
}).into()).check(), Ok(())); }).into()).check(), Ok(()));
assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: 100, balancing_value: 100,
outputs: vec![Default::default()], outputs: vec![Default::default()],
spends: vec![Default::default()], spends: vec![Default::default()],
..Default::default() ..Default::default()
}).into()).check(), Ok(())); }).into()).check(), Ok(()));
assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling { assert_eq!(TransactionSapling::new(&test_data::TransactionBuilder::with_sapling(Sapling {
amount: 100, balancing_value: 100,
..Default::default() ..Default::default()
}).into()).check(), Err(TransactionError::EmptySaplingHasBalance)); }).into()).check(), Err(TransactionError::EmptySaplingHasBalance));
} }