Merge pull request #1713 from ZcashFoundation/use-groth16-batch-math
Use batch optimizations, load params in groth16::Verifier, verify Spend & Output descriptions in transaction verifier
This commit is contained in:
parent
e0643f3664
commit
7efc700aca
|
@ -227,21 +227,20 @@ checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bellman"
|
name = "bellman"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/zkcrypto/bellman?rev=bd4af09f50a4d020a3672aff37c4f3f2da2bb36b#bd4af09f50a4d020a3672aff37c4f3f2da2bb36b"
|
||||||
checksum = "7089887635778eabf0038a166f586eee5413fb85c8fa6c9a754914f0f644f49f"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec 0.18.4",
|
"bitvec 0.20.1",
|
||||||
"blake2s_simd",
|
"blake2s_simd",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"ff 0.8.0",
|
"ff",
|
||||||
"futures 0.1.30",
|
"futures 0.1.30",
|
||||||
"futures-cpupool",
|
"futures-cpupool",
|
||||||
"group 0.8.0",
|
"group",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pairing",
|
"pairing",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.6.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -319,17 +318,6 @@ dependencies = [
|
||||||
"radium 0.3.0",
|
"radium 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitvec"
|
|
||||||
version = "0.18.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d2838fdd79e8776dbe07a106c784b0f8dda571a21b2750a092cc4cbaa653c8e"
|
|
||||||
dependencies = [
|
|
||||||
"funty",
|
|
||||||
"radium 0.4.1",
|
|
||||||
"wyz",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitvec"
|
name = "bitvec"
|
||||||
version = "0.20.1"
|
version = "0.20.1"
|
||||||
|
@ -403,27 +391,15 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bls12_381"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4caf0101205582491f772d60a6fcb6bcec19963e68209cb631851eeadb01421f"
|
|
||||||
dependencies = [
|
|
||||||
"bitvec 0.18.4",
|
|
||||||
"ff 0.8.0",
|
|
||||||
"group 0.8.0",
|
|
||||||
"pairing",
|
|
||||||
"rand_core 0.5.1",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bls12_381"
|
name = "bls12_381"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c56609cc42c628848e7b18e0baf42a4ef626b8c50442dc08b8094bd21d8ad32"
|
checksum = "1c56609cc42c628848e7b18e0baf42a4ef626b8c50442dc08b8094bd21d8ad32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ff 0.9.0",
|
"ff",
|
||||||
|
"group",
|
||||||
|
"pairing",
|
||||||
"rand_core 0.6.1",
|
"rand_core 0.6.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
@ -1007,17 +983,6 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ff"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "01646e077d4ebda82b73f1bca002ea1e91561a77df2431a9e79729bcc31950ef"
|
|
||||||
dependencies = [
|
|
||||||
"bitvec 0.18.4",
|
|
||||||
"rand_core 0.5.1",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -1280,18 +1245,6 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "group"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc11f9f5fbf1943b48ae7c2bf6846e7d827a512d1be4f23af708f5ca5d01dde1"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"ff 0.8.0",
|
|
||||||
"rand_core 0.5.1",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "group"
|
name = "group"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -1299,7 +1252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61b3c1e8b4f1ca07e6605ea1be903a5f6956aec5c8a67fd44d56076631675ed8"
|
checksum = "61b3c1e8b4f1ca07e6605ea1be903a5f6956aec5c8a67fd44d56076631675ed8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"ff 0.9.0",
|
"ff",
|
||||||
"rand_core 0.6.1",
|
"rand_core 0.6.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
@ -1667,8 +1620,8 @@ checksum = "4d7e7fef85ae7b26dd89f34175b7f3c5ace64067a110c2ac86cf92407a6666ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec 0.20.1",
|
"bitvec 0.20.1",
|
||||||
"bls12_381 0.4.0",
|
"bls12_381 0.4.0",
|
||||||
"ff 0.9.0",
|
"ff",
|
||||||
"group 0.9.0",
|
"group",
|
||||||
"rand_core 0.6.1",
|
"rand_core 0.6.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
@ -2077,12 +2030,12 @@ checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pairing"
|
name = "pairing"
|
||||||
version = "0.18.0"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f702cdbee9e0a6272452c20dec82465bc821116598b4eeb63e9a71a69dbf7fd"
|
checksum = "9be899ebf10363f018353dba1baabb7e83145f3683c7b83b73b93b563e3167cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ff 0.8.0",
|
"ff",
|
||||||
"group 0.8.0",
|
"group",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2346,12 +2299,6 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac"
|
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "radium"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "64de9a0c5361e034f1aefc9f71a86871ec870e766fe31a009734a989b329286a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "radium"
|
name = "radium"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
@ -2381,7 +2328,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.2.2",
|
"rand_chacha 0.2.2",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
"rand_hc",
|
"rand_hc 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2393,6 +2340,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.3.0",
|
"rand_chacha 0.3.0",
|
||||||
"rand_core 0.6.1",
|
"rand_core 0.6.1",
|
||||||
|
"rand_hc 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2457,6 +2405,15 @@ dependencies = [
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_xorshift"
|
name = "rand_xorshift"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -2512,14 +2469,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redjubjub"
|
name = "redjubjub"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/ZcashFoundation/redjubjub?rev=8101eaff1cb2fca45334f77a65caa4c46e3d545b#8101eaff1cb2fca45334f77a65caa4c46e3d545b"
|
||||||
checksum = "63266b4e0d748bf7514f794bcdb1db681a22b11ed4dea3984a3148d6cd5733c3"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"digest 0.9.0",
|
"digest 0.9.0",
|
||||||
"jubjub 0.3.0",
|
"jubjub 0.3.0",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.6.1",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -3776,6 +3732,56 @@ dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c904628658374e651288f000934c33ef738b2d8b3e65d4100b70b395dbe2bb"
|
||||||
|
dependencies = [
|
||||||
|
"wagyu-zcash-parameters-1",
|
||||||
|
"wagyu-zcash-parameters-2",
|
||||||
|
"wagyu-zcash-parameters-3",
|
||||||
|
"wagyu-zcash-parameters-4",
|
||||||
|
"wagyu-zcash-parameters-5",
|
||||||
|
"wagyu-zcash-parameters-6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters-1"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bf2e21bb027d3f8428c60d6a720b54a08bf6ce4e6f834ef8e0d38bb5695da8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters-2"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a616ab2e51e74cc48995d476e94de810fb16fc73815f390bf2941b046cc9ba2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters-3"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14da1e2e958ff93c0830ee68e91884069253bf3462a67831b02b367be75d6147"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters-4"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f058aeef03a2070e8666ffb5d1057d8bb10313b204a254a6e6103eb958e9a6d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters-5"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ffe916b30e608c032ae1b734f02574a3e12ec19ab5cc5562208d679efe4969d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wagyu-zcash-parameters-6"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7b6d5a78adc3e8f198e9cd730f219a695431467f7ec29dcfc63ade885feebe1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wait-timeout"
|
name = "wait-timeout"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -4053,7 +4059,7 @@ dependencies = [
|
||||||
"primitive-types",
|
"primitive-types",
|
||||||
"proptest",
|
"proptest",
|
||||||
"proptest-derive",
|
"proptest-derive",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.6.1",
|
||||||
"redjubjub",
|
"redjubjub",
|
||||||
"ripemd160",
|
"ripemd160",
|
||||||
"secp256k1",
|
"secp256k1",
|
||||||
|
@ -4076,18 +4082,19 @@ name = "zebra-consensus"
|
||||||
version = "1.0.0-alpha.5"
|
version = "1.0.0-alpha.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman",
|
"bellman",
|
||||||
"bls12_381 0.3.1",
|
"blake2b_simd",
|
||||||
|
"bls12_381 0.4.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"futures 0.3.13",
|
"futures 0.3.13",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"jubjub 0.6.0",
|
"jubjub 0.6.0",
|
||||||
|
"lazy_static",
|
||||||
"metrics",
|
"metrics",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pairing",
|
"pairing",
|
||||||
"rand 0.7.3",
|
"rand 0.8.1",
|
||||||
"redjubjub",
|
|
||||||
"serde",
|
"serde",
|
||||||
"spandoc",
|
"spandoc",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -4099,6 +4106,7 @@ dependencies = [
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
"tracing-subscriber 0.2.17",
|
"tracing-subscriber 0.2.17",
|
||||||
|
"wagyu-zcash-parameters",
|
||||||
"zebra-chain",
|
"zebra-chain",
|
||||||
"zebra-script",
|
"zebra-script",
|
||||||
"zebra-state",
|
"zebra-state",
|
||||||
|
@ -4121,7 +4129,7 @@ dependencies = [
|
||||||
"pin-project 0.4.27",
|
"pin-project 0.4.27",
|
||||||
"proptest",
|
"proptest",
|
||||||
"proptest-derive",
|
"proptest-derive",
|
||||||
"rand 0.7.3",
|
"rand 0.8.1",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -4236,7 +4244,7 @@ dependencies = [
|
||||||
"metrics-exporter-prometheus",
|
"metrics-exporter-prometheus",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pin-project 0.4.27",
|
"pin-project 0.4.27",
|
||||||
"rand 0.7.3",
|
"rand 0.8.1",
|
||||||
"sentry",
|
"sentry",
|
||||||
"sentry-tracing",
|
"sentry-tracing",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -6,21 +6,24 @@ license = "MIT"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "0.3.6", features = ["time", "sync", "stream", "tracing", "macros"] }
|
futures = "0.3.13"
|
||||||
tower = { version = "0.4", features = ["util", "buffer"] }
|
|
||||||
futures-core = "0.3.13"
|
futures-core = "0.3.13"
|
||||||
pin-project = "0.4.27"
|
pin-project = "0.4.27"
|
||||||
|
tokio = { version = "0.3.6", features = ["time", "sync", "stream", "tracing", "macros"] }
|
||||||
|
tower = { version = "0.4", features = ["util", "buffer"] }
|
||||||
tracing = "0.1.25"
|
tracing = "0.1.25"
|
||||||
tracing-futures = "0.2.5"
|
tracing-futures = "0.2.5"
|
||||||
futures = "0.3.13"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
color-eyre = "0.5.10"
|
||||||
ed25519-zebra = "2.1.0"
|
ed25519-zebra = "2.1.0"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
tokio = { version = "0.3.6", features = ["full"]}
|
tokio = { version = "0.3.6", features = ["full"]}
|
||||||
|
tokio-test = "0.4.1"
|
||||||
|
tower-fallback = { path = "../tower-fallback/" }
|
||||||
|
tower-test = "0.4.0"
|
||||||
tracing = "0.1.25"
|
tracing = "0.1.25"
|
||||||
zebra-test = { path = "../zebra-test/" }
|
zebra-test = { path = "../zebra-test/" }
|
||||||
tower-fallback = { path = "../tower-fallback/" }
|
|
||||||
color-eyre = "0.5.10"
|
|
||||||
tokio-test = "0.4.1"
|
|
||||||
tower-test = "0.4.0"
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ hex = "0.4"
|
||||||
jubjub = "0.6.0"
|
jubjub = "0.6.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
primitive-types = "0.9.0"
|
primitive-types = "0.9.0"
|
||||||
rand_core = "0.5.1"
|
rand_core = "0.6"
|
||||||
ripemd160 = "0.8.0"
|
ripemd160 = "0.8.0"
|
||||||
secp256k1 = { version = "0.20.1", features = ["serde"] }
|
secp256k1 = { version = "0.20.1", features = ["serde"] }
|
||||||
serde = { version = "1", features = ["serde_derive", "rc"] }
|
serde = { version = "1", features = ["serde_derive", "rc"] }
|
||||||
|
@ -41,7 +41,8 @@ proptest-derive = { version = "0.3.0", optional = true }
|
||||||
displaydoc = "0.2.0"
|
displaydoc = "0.2.0"
|
||||||
ed25519-zebra = "2"
|
ed25519-zebra = "2"
|
||||||
equihash = "0.1"
|
equihash = "0.1"
|
||||||
redjubjub = "0.2"
|
#redjubjub = "0.2"
|
||||||
|
redjubjub = {git = "https://github.com/ZcashFoundation/redjubjub", rev = "8101eaff1cb2fca45334f77a65caa4c46e3d545b"}
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -27,14 +27,26 @@ impl Clone for Groth16Proof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for Groth16Proof {}
|
||||||
|
|
||||||
|
impl From<[u8; 192]> for Groth16Proof {
|
||||||
|
fn from(bytes: [u8; 192]) -> Groth16Proof {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Groth16Proof> for [u8; 192] {
|
||||||
|
fn from(rt: Groth16Proof) -> [u8; 192] {
|
||||||
|
rt.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for Groth16Proof {
|
impl PartialEq for Groth16Proof {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.0[..] == other.0[..]
|
self.0[..] == other.0[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Groth16Proof {}
|
|
||||||
|
|
||||||
impl ZcashSerialize for Groth16Proof {
|
impl ZcashSerialize for Groth16Proof {
|
||||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
writer.write_all(&self.0[..])?;
|
writer.write_all(&self.0[..])?;
|
||||||
|
|
|
@ -885,6 +885,12 @@ impl fmt::Debug for EphemeralPublicKey {
|
||||||
|
|
||||||
impl Eq for EphemeralPublicKey {}
|
impl Eq for EphemeralPublicKey {}
|
||||||
|
|
||||||
|
impl From<EphemeralPublicKey> for [u8; 32] {
|
||||||
|
fn from(nk: EphemeralPublicKey) -> [u8; 32] {
|
||||||
|
nk.0.to_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&EphemeralPublicKey> for [u8; 32] {
|
impl From<&EphemeralPublicKey> for [u8; 32] {
|
||||||
fn from(nk: &EphemeralPublicKey) -> [u8; 32] {
|
fn from(nk: &EphemeralPublicKey) -> [u8; 32] {
|
||||||
nk.0.to_bytes()
|
nk.0.to_bytes()
|
||||||
|
|
|
@ -52,3 +52,41 @@ impl From<Nullifier> for [u8; 32] {
|
||||||
n.0
|
n.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Nullifier> for [jubjub::Fq; 2] {
|
||||||
|
/// Add the nullifier through multiscalar packing
|
||||||
|
///
|
||||||
|
/// Informed by https://github.com/zkcrypto/bellman/blob/main/src/gadgets/multipack.rs
|
||||||
|
fn from(n: Nullifier) -> Self {
|
||||||
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
|
let nullifier_bits_le: Vec<bool> =
|
||||||
|
n.0.iter()
|
||||||
|
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// The number of bits needed to represent the modulus, minus 1.
|
||||||
|
const CAPACITY: usize = 255 - 1;
|
||||||
|
|
||||||
|
let mut result = [jubjub::Fq::zero(); 2];
|
||||||
|
|
||||||
|
// Since we know the max bits of the input (256) and the chunk size
|
||||||
|
// (254), this will always result in 2 chunks.
|
||||||
|
for (i, bits) in nullifier_bits_le.chunks(CAPACITY).enumerate() {
|
||||||
|
let mut cur = jubjub::Fq::zero();
|
||||||
|
let mut coeff = jubjub::Fq::one();
|
||||||
|
|
||||||
|
for bit in bits {
|
||||||
|
if *bit {
|
||||||
|
cur.add_assign(&coeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
coeff = coeff.double();
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = cur
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,30 @@ pub struct Output {
|
||||||
pub zkproof: Groth16Proof,
|
pub zkproof: Groth16Proof,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Output {
|
||||||
|
/// Encodes the primary inputs for the proof statement as 5 Bls12_381 base
|
||||||
|
/// field elements, to match bellman::groth16::verify_proof.
|
||||||
|
///
|
||||||
|
/// NB: jubjub::Fq is a type alias for bls12_381::Scalar.
|
||||||
|
///
|
||||||
|
/// https://zips.z.cash/protocol/protocol.pdf#cctsaplingoutput
|
||||||
|
pub fn primary_inputs(&self) -> Vec<jubjub::Fq> {
|
||||||
|
let mut inputs = vec![];
|
||||||
|
|
||||||
|
let cv_affine = jubjub::AffinePoint::from_bytes(self.cv.into()).unwrap();
|
||||||
|
inputs.push(cv_affine.get_u());
|
||||||
|
inputs.push(cv_affine.get_v());
|
||||||
|
|
||||||
|
let epk_affine = jubjub::AffinePoint::from_bytes(self.ephemeral_key.into()).unwrap();
|
||||||
|
inputs.push(epk_affine.get_u());
|
||||||
|
inputs.push(epk_affine.get_v());
|
||||||
|
|
||||||
|
inputs.push(self.cm_u);
|
||||||
|
|
||||||
|
inputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ZcashSerialize for Output {
|
impl ZcashSerialize for Output {
|
||||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
self.cv.zcash_serialize(&mut writer)?;
|
self.cv.zcash_serialize(&mut writer)?;
|
||||||
|
|
|
@ -31,6 +31,35 @@ pub struct Spend {
|
||||||
pub spend_auth_sig: redjubjub::Signature<SpendAuth>,
|
pub spend_auth_sig: redjubjub::Signature<SpendAuth>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Spend {
|
||||||
|
/// Encodes the primary inputs for the proof statement as 7 Bls12_381 base
|
||||||
|
/// field elements, to match bellman::groth16::verify_proof.
|
||||||
|
///
|
||||||
|
/// NB: jubjub::Fq is a type alias for bls12_381::Scalar.
|
||||||
|
///
|
||||||
|
/// https://zips.z.cash/protocol/protocol.pdf#cctsaplingspend
|
||||||
|
pub fn primary_inputs(&self) -> Vec<jubjub::Fq> {
|
||||||
|
let mut inputs = vec![];
|
||||||
|
|
||||||
|
let rk_affine = jubjub::AffinePoint::from_bytes(self.rk.into()).unwrap();
|
||||||
|
inputs.push(rk_affine.get_u());
|
||||||
|
inputs.push(rk_affine.get_v());
|
||||||
|
|
||||||
|
let cv_affine = jubjub::AffinePoint::from_bytes(self.cv.into()).unwrap();
|
||||||
|
inputs.push(cv_affine.get_u());
|
||||||
|
inputs.push(cv_affine.get_v());
|
||||||
|
|
||||||
|
inputs.push(jubjub::Fq::from_bytes(&self.anchor.into()).unwrap());
|
||||||
|
|
||||||
|
let nullifier_limbs: [jubjub::Fq; 2] = self.nullifier.into();
|
||||||
|
|
||||||
|
inputs.push(nullifier_limbs[0]);
|
||||||
|
inputs.push(nullifier_limbs[1]);
|
||||||
|
|
||||||
|
inputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ZcashSerialize for Spend {
|
impl ZcashSerialize for Spend {
|
||||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
self.cv.zcash_serialize(&mut writer)?;
|
self.cv.zcash_serialize(&mut writer)?;
|
||||||
|
|
|
@ -84,6 +84,18 @@ impl fmt::Debug for Root {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 32]> for Root {
|
||||||
|
fn from(bytes: [u8; 32]) -> Root {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Root> for [u8; 32] {
|
||||||
|
fn from(root: Root) -> Self {
|
||||||
|
root.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sapling Note Commitment Tree
|
/// Sapling Note Commitment Tree
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
struct NoteCommitmentTree {
|
struct NoteCommitmentTree {
|
||||||
|
|
|
@ -6,15 +6,18 @@ license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
blake2b_simd = "0.5.11"
|
||||||
|
#bellman = "0.8"
|
||||||
|
bellman = { git = "https://github.com/zkcrypto/bellman", rev = "bd4af09f50a4d020a3672aff37c4f3f2da2bb36b" }
|
||||||
|
bls12_381 = "0.4.0"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
displaydoc = "0.2.0"
|
displaydoc = "0.2.0"
|
||||||
jubjub = "0.6.0"
|
jubjub = "0.6.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
once_cell = "1.7"
|
once_cell = "1.7"
|
||||||
rand = "0.7"
|
pairing = "0.19.0"
|
||||||
redjubjub = "0.2"
|
rand = "0.8"
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
serde = { version = "1", features = ["serde_derive"] }
|
||||||
bellman = "0.8"
|
|
||||||
bls12_381 = "0.3.1"
|
|
||||||
|
|
||||||
futures = "0.3.13"
|
futures = "0.3.13"
|
||||||
futures-util = "0.3.6"
|
futures-util = "0.3.6"
|
||||||
|
@ -30,11 +33,10 @@ tower-batch = { path = "../tower-batch/" }
|
||||||
zebra-chain = { path = "../zebra-chain" }
|
zebra-chain = { path = "../zebra-chain" }
|
||||||
zebra-state = { path = "../zebra-state" }
|
zebra-state = { path = "../zebra-state" }
|
||||||
zebra-script = { path = "../zebra-script" }
|
zebra-script = { path = "../zebra-script" }
|
||||||
pairing = "0.18.0"
|
wagyu-zcash-parameters = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
color-eyre = "0.5.10"
|
color-eyre = "0.5.10"
|
||||||
rand = "0.7"
|
|
||||||
spandoc = "0.2"
|
spandoc = "0.2"
|
||||||
tokio = { version = "0.3.6", features = ["full"] }
|
tokio = { version = "0.3.6", features = ["full"] }
|
||||||
tracing-error = "0.1.2"
|
tracing-error = "0.1.2"
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use zebra_chain::{block, primitives::ed25519};
|
|
||||||
|
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
|
@ -55,6 +53,9 @@ pub enum TransactionError {
|
||||||
#[error("could not verify a transparent script")]
|
#[error("could not verify a transparent script")]
|
||||||
Script(#[from] zebra_script::Error),
|
Script(#[from] zebra_script::Error),
|
||||||
|
|
||||||
|
#[error("spend description cv and rk MUST NOT be of small order")]
|
||||||
|
SmallOrder,
|
||||||
|
|
||||||
// XXX change this when we align groth16 verifier errors with bellman
|
// XXX change this when we align groth16 verifier errors with bellman
|
||||||
// and add a from annotation when the error type is more precise
|
// and add a from annotation when the error type is more precise
|
||||||
#[error("spend proof MUST be valid given a primary input formed from the other fields except spendAuthSig")]
|
#[error("spend proof MUST be valid given a primary input formed from the other fields except spendAuthSig")]
|
||||||
|
@ -63,10 +64,10 @@ pub enum TransactionError {
|
||||||
#[error(
|
#[error(
|
||||||
"joinSplitSig MUST represent a valid signature under joinSplitPubKey of dataToBeSigned"
|
"joinSplitSig MUST represent a valid signature under joinSplitPubKey of dataToBeSigned"
|
||||||
)]
|
)]
|
||||||
Ed25519(#[from] ed25519::Error),
|
Ed25519(#[from] zebra_chain::primitives::ed25519::Error),
|
||||||
|
|
||||||
#[error("bindingSig MUST represent a valid signature under the transaction binding validating key bvk of SigHash")]
|
#[error("bindingSig MUST represent a valid signature under the transaction binding validating key bvk of SigHash")]
|
||||||
RedJubjub(redjubjub::Error),
|
RedJubjub(zebra_chain::primitives::redjubjub::Error),
|
||||||
|
|
||||||
// temporary error type until #1186 is fixed
|
// temporary error type until #1186 is fixed
|
||||||
#[error("Downcast from BoxError to redjubjub::Error failed")]
|
#[error("Downcast from BoxError to redjubjub::Error failed")]
|
||||||
|
@ -75,7 +76,7 @@ pub enum TransactionError {
|
||||||
|
|
||||||
impl From<BoxError> for TransactionError {
|
impl From<BoxError> for TransactionError {
|
||||||
fn from(err: BoxError) -> Self {
|
fn from(err: BoxError) -> Self {
|
||||||
match err.downcast::<redjubjub::Error>() {
|
match err.downcast::<zebra_chain::primitives::redjubjub::Error>() {
|
||||||
Ok(e) => TransactionError::RedJubjub(*e),
|
Ok(e) => TransactionError::RedJubjub(*e),
|
||||||
Err(e) => TransactionError::InternalDowncastError(format!(
|
Err(e) => TransactionError::InternalDowncastError(format!(
|
||||||
"downcast to redjubjub::Error failed, original error: {:?}",
|
"downcast to redjubjub::Error failed, original error: {:?}",
|
||||||
|
@ -101,8 +102,8 @@ pub enum BlockError {
|
||||||
|
|
||||||
#[error("block has mismatched merkle root")]
|
#[error("block has mismatched merkle root")]
|
||||||
BadMerkleRoot {
|
BadMerkleRoot {
|
||||||
actual: block::merkle::Root,
|
actual: zebra_chain::block::merkle::Root,
|
||||||
expected: block::merkle::Root,
|
expected: zebra_chain::block::merkle::Root,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("block contains duplicate transactions")]
|
#[error("block contains duplicate transactions")]
|
||||||
|
|
|
@ -51,7 +51,6 @@ mod checkpoint;
|
||||||
mod config;
|
mod config;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod parameters;
|
mod parameters;
|
||||||
#[allow(dead_code)] // Remove this once transaction verification is implemented
|
|
||||||
mod primitives;
|
mod primitives;
|
||||||
mod script;
|
mod script;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
|
|
|
@ -9,120 +9,119 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use bellman::{
|
use bellman::{
|
||||||
groth16::{PreparedVerifyingKey, Proof},
|
groth16::{batch, prepare_verifying_key, VerifyingKey},
|
||||||
VerificationError,
|
VerificationError,
|
||||||
};
|
};
|
||||||
use bls12_381::Bls12;
|
use bls12_381::Bls12;
|
||||||
use pairing::MultiMillerLoop;
|
use futures::future::{ready, Ready};
|
||||||
use rand::{thread_rng, CryptoRng, RngCore};
|
use once_cell::sync::Lazy;
|
||||||
|
use rand::thread_rng;
|
||||||
use tokio::sync::broadcast::{channel, error::RecvError, Sender};
|
use tokio::sync::broadcast::{channel, error::RecvError, Sender};
|
||||||
use tower::Service;
|
use tower::{util::ServiceFn, Service};
|
||||||
use tower_batch::BatchControl;
|
use tower_batch::{Batch, BatchControl};
|
||||||
use tower_fallback::Fallback;
|
use tower_fallback::Fallback;
|
||||||
|
use zebra_chain::sapling::{Output, Spend};
|
||||||
|
|
||||||
use crate::BoxError;
|
mod hash_reader;
|
||||||
|
mod params;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
// === TEMPORARY BATCH BELLMAN SUBSTITUTE ===
|
use self::hash_reader::HashReader;
|
||||||
// These types are meant to be API compatible with the work in progress batch
|
use params::PARAMS;
|
||||||
// verification API being implemented in Bellman. Once we've finished that
|
|
||||||
// implementation and upgraded our dependency, we should be able to remove this
|
|
||||||
// section of code and replace each of these types with the commented out items
|
|
||||||
// from the rest of this file.
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
/// Global batch verification context for Groth16 proofs of Spend statements.
|
||||||
pub struct Item<E: MultiMillerLoop> {
|
///
|
||||||
proof: Proof<E>,
|
/// This service transparently batches contemporaneous proof verifications,
|
||||||
public_inputs: Vec<E::Fr>,
|
/// handling batch failures by falling back to individual verification.
|
||||||
}
|
///
|
||||||
|
/// Note that making a `Service` call requires mutable access to the service, so
|
||||||
|
/// you should call `.clone()` on the global handle to create a local, mutable
|
||||||
|
/// handle.
|
||||||
|
pub static SPEND_VERIFIER: Lazy<
|
||||||
|
Fallback<Batch<Verifier, Item>, ServiceFn<fn(Item) -> Ready<Result<(), VerificationError>>>>,
|
||||||
|
> = Lazy::new(|| {
|
||||||
|
Fallback::new(
|
||||||
|
Batch::new(
|
||||||
|
Verifier::new(&PARAMS.sapling.spend.vk),
|
||||||
|
super::MAX_BATCH_SIZE,
|
||||||
|
super::MAX_BATCH_LATENCY,
|
||||||
|
),
|
||||||
|
// We want to fallback to individual verification if batch verification
|
||||||
|
// fails, so we need a Service to use. The obvious way to do this would
|
||||||
|
// be to write a closure that returns an async block. But because we
|
||||||
|
// have to specify the type of a static, we need to be able to write the
|
||||||
|
// type of the closure and its return value, and both closures and async
|
||||||
|
// blocks have eldritch types whose names cannot be written. So instead,
|
||||||
|
// we use a Ready to avoid an async block and cast the closure to a
|
||||||
|
// function (which is possible because it doesn't capture any state).
|
||||||
|
tower::service_fn(
|
||||||
|
(|item: Item| {
|
||||||
|
ready(item.verify_single(&prepare_verifying_key(&PARAMS.sapling.spend.vk)))
|
||||||
|
}) as fn(_) -> _,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
impl<E: MultiMillerLoop> Item<E> {
|
/// Global batch verification context for Groth16 proofs of Output statements.
|
||||||
fn verify_single(self, pvk: &PreparedVerifyingKey<E>) -> Result<(), VerificationError> {
|
///
|
||||||
let Item {
|
/// This service transparently batches contemporaneous proof verifications,
|
||||||
proof,
|
/// handling batch failures by falling back to individual verification.
|
||||||
public_inputs,
|
///
|
||||||
} = self;
|
/// Note that making a `Service` call requires mutable access to the service, so
|
||||||
|
/// you should call `.clone()` on the global handle to create a local, mutable
|
||||||
|
/// handle.
|
||||||
|
pub static OUTPUT_VERIFIER: Lazy<
|
||||||
|
Fallback<Batch<Verifier, Item>, ServiceFn<fn(Item) -> Ready<Result<(), VerificationError>>>>,
|
||||||
|
> = Lazy::new(|| {
|
||||||
|
Fallback::new(
|
||||||
|
Batch::new(
|
||||||
|
Verifier::new(&PARAMS.sapling.output.vk),
|
||||||
|
super::MAX_BATCH_SIZE,
|
||||||
|
super::MAX_BATCH_LATENCY,
|
||||||
|
),
|
||||||
|
// We want to fallback to individual verification if batch verification
|
||||||
|
// fails, so we need a Service to use. The obvious way to do this would
|
||||||
|
// be to write a closure that returns an async block. But because we
|
||||||
|
// have to specify the type of a static, we need to be able to write the
|
||||||
|
// type of the closure and its return value, and both closures and async
|
||||||
|
// blocks have eldritch types whose names cannot be written. So instead,
|
||||||
|
// we use a Ready to avoid an async block and cast the closure to a
|
||||||
|
// function (which is possible because it doesn't capture any state).
|
||||||
|
tower::service_fn(
|
||||||
|
(|item: Item| {
|
||||||
|
ready(item.verify_single(&prepare_verifying_key(&PARAMS.sapling.output.vk)))
|
||||||
|
}) as fn(_) -> _,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
bellman::groth16::verify_proof(pvk, &proof, &public_inputs)
|
/// A Groth16 verification item, used as the request type of the service.
|
||||||
|
pub type Item = batch::Item<Bls12>;
|
||||||
|
|
||||||
|
pub struct ItemWrapper(Item);
|
||||||
|
|
||||||
|
impl From<&Spend> for ItemWrapper {
|
||||||
|
fn from(spend: &Spend) -> Self {
|
||||||
|
Self(Item::from((
|
||||||
|
bellman::groth16::Proof::read(&spend.zkproof.0[..]).unwrap(),
|
||||||
|
spend.primary_inputs(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: MultiMillerLoop> From<(&Proof<E>, &[E::Fr])> for Item<E> {
|
impl From<&Output> for ItemWrapper {
|
||||||
fn from((proof, public_inputs): (&Proof<E>, &[E::Fr])) -> Self {
|
fn from(output: &Output) -> Self {
|
||||||
(proof.clone(), public_inputs.to_owned()).into()
|
Self(Item::from((
|
||||||
|
bellman::groth16::Proof::read(&output.zkproof.0[..]).unwrap(),
|
||||||
|
output.primary_inputs(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: MultiMillerLoop> From<(Proof<E>, Vec<E::Fr>)> for Item<E> {
|
impl From<ItemWrapper> for Item {
|
||||||
fn from((proof, public_inputs): (Proof<E>, Vec<E::Fr>)) -> Self {
|
fn from(item_wrapper: ItemWrapper) -> Self {
|
||||||
Self {
|
item_wrapper.0
|
||||||
proof,
|
|
||||||
public_inputs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Batch {
|
|
||||||
queue: Vec<Item<Bls12>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Batch {
|
|
||||||
fn queue(&mut self, item: Item<Bls12>) {
|
|
||||||
self.queue.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify<R: RngCore + CryptoRng>(
|
|
||||||
self,
|
|
||||||
_rng: R,
|
|
||||||
pvk: &PreparedVerifyingKey<Bls12>,
|
|
||||||
) -> Result<(), VerificationError> {
|
|
||||||
for item in self.queue {
|
|
||||||
item.verify_single(pvk)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// === TEMPORARY BATCH BELLMAN SUBSTITUTE END ===
|
|
||||||
|
|
||||||
// /// A Groth16 verification item, used as the request type of the service.
|
|
||||||
// pub type Item = batch::Item<Bls12>;
|
|
||||||
|
|
||||||
/// Groth16 signature verifier service
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Verifier {
|
|
||||||
inner: Fallback<tower_batch::Batch<VerifierImpl, Item<Bls12>>, FallbackVerifierImpl>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Verifier {
|
|
||||||
/// Constructs a new verifier.
|
|
||||||
pub fn new(pvk: &'static PreparedVerifyingKey<Bls12>) -> Self {
|
|
||||||
let verifier_impl = VerifierImpl::new(pvk);
|
|
||||||
let fallback_impl = FallbackVerifierImpl::new(pvk);
|
|
||||||
|
|
||||||
let max_items = super::MAX_BATCH_SIZE;
|
|
||||||
let max_latency = super::MAX_BATCH_LATENCY;
|
|
||||||
|
|
||||||
let inner = tower_batch::Batch::new(verifier_impl, max_items, max_latency);
|
|
||||||
let inner = Fallback::new(inner, fallback_impl);
|
|
||||||
|
|
||||||
Self { inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Service<Item<Bls12>> for Verifier {
|
|
||||||
type Response = ();
|
|
||||||
type Error = BoxError;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), BoxError>> + Send + 'static>>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
||||||
self.inner.poll_ready(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, req: Item<Bls12>) -> Self::Future {
|
|
||||||
use futures::FutureExt;
|
|
||||||
self.inner.call(req).boxed()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,37 +130,35 @@ impl Service<Item<Bls12>> for Verifier {
|
||||||
/// This is the core implementation for the batch verification logic of the groth
|
/// This is the core implementation for the batch verification logic of the groth
|
||||||
/// verifier. It handles batching incoming requests, driving batches to
|
/// verifier. It handles batching incoming requests, driving batches to
|
||||||
/// completion, and reporting results.
|
/// completion, and reporting results.
|
||||||
struct VerifierImpl {
|
pub struct Verifier {
|
||||||
// batch: batch::Verifier<Bls12>,
|
batch: batch::Verifier<Bls12>,
|
||||||
batch: Batch,
|
|
||||||
// Making this 'static makes managing lifetimes much easier.
|
// Making this 'static makes managing lifetimes much easier.
|
||||||
pvk: &'static PreparedVerifyingKey<Bls12>,
|
vk: &'static VerifyingKey<Bls12>,
|
||||||
/// Broadcast sender used to send the result of a batch verification to each
|
/// Broadcast sender used to send the result of a batch verification to each
|
||||||
/// request source in the batch.
|
/// request source in the batch.
|
||||||
tx: Sender<Result<(), VerificationError>>,
|
tx: Sender<Result<(), VerificationError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerifierImpl {
|
impl Verifier {
|
||||||
fn new(pvk: &'static PreparedVerifyingKey<Bls12>) -> Self {
|
fn new(vk: &'static VerifyingKey<Bls12>) -> Self {
|
||||||
// let batch = batch::Verifier::default();
|
let batch = batch::Verifier::default();
|
||||||
let batch = Batch::default();
|
|
||||||
let (tx, _) = channel(super::BROADCAST_BUFFER_SIZE);
|
let (tx, _) = channel(super::BROADCAST_BUFFER_SIZE);
|
||||||
Self { batch, pvk, tx }
|
Self { batch, vk, tx }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for VerifierImpl {
|
impl fmt::Debug for Verifier {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let name = "VerifierImpl";
|
let name = "Verifier";
|
||||||
f.debug_struct(name)
|
f.debug_struct(name)
|
||||||
.field("batch", &"..")
|
.field("batch", &"..")
|
||||||
.field("pvk", &"..")
|
.field("vk", &"..")
|
||||||
.field("tx", &self.tx)
|
.field("tx", &self.tx)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service<BatchControl<Item<Bls12>>> for VerifierImpl {
|
impl Service<BatchControl<Item>> for Verifier {
|
||||||
type Response = ();
|
type Response = ();
|
||||||
type Error = VerificationError;
|
type Error = VerificationError;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), VerificationError>> + Send + 'static>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), VerificationError>> + Send + 'static>>;
|
||||||
|
@ -170,7 +167,7 @@ impl Service<BatchControl<Item<Bls12>>> for VerifierImpl {
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: BatchControl<Item<Bls12>>) -> Self::Future {
|
fn call(&mut self, req: BatchControl<Item>) -> Self::Future {
|
||||||
match req {
|
match req {
|
||||||
BatchControl::Item(item) => {
|
BatchControl::Item(item) => {
|
||||||
tracing::trace!("got item");
|
tracing::trace!("got item");
|
||||||
|
@ -193,52 +190,17 @@ impl Service<BatchControl<Item<Bls12>>> for VerifierImpl {
|
||||||
BatchControl::Flush => {
|
BatchControl::Flush => {
|
||||||
tracing::trace!("got flush command");
|
tracing::trace!("got flush command");
|
||||||
let batch = mem::take(&mut self.batch);
|
let batch = mem::take(&mut self.batch);
|
||||||
let _ = self.tx.send(batch.verify(thread_rng(), self.pvk));
|
let _ = self.tx.send(batch.verify(thread_rng(), self.vk));
|
||||||
Box::pin(async { Ok(()) })
|
Box::pin(async { Ok(()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VerifierImpl {
|
impl Drop for Verifier {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// We need to flush the current batch in case there are still any pending futures.
|
// We need to flush the current batch in case there are still any pending futures.
|
||||||
let batch = mem::take(&mut self.batch);
|
let batch = mem::take(&mut self.batch);
|
||||||
let _ = self.tx.send(batch.verify(thread_rng(), self.pvk));
|
let _ = self.tx.send(batch.verify(thread_rng(), self.vk));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Groth16 signature verifier fallback implementation
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct FallbackVerifierImpl {
|
|
||||||
pvk: &'static PreparedVerifyingKey<Bls12>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FallbackVerifierImpl {
|
|
||||||
fn new(pvk: &'static PreparedVerifyingKey<Bls12>) -> Self {
|
|
||||||
Self { pvk }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for FallbackVerifierImpl {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let name = "FallbackVerifierImpl";
|
|
||||||
f.debug_struct(name).field("pvk", &"..").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Service<Item<Bls12>> for FallbackVerifierImpl {
|
|
||||||
type Response = ();
|
|
||||||
type Error = VerificationError;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), VerificationError>> + Send + 'static>>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, item: Item<Bls12>) -> Self::Future {
|
|
||||||
tracing::trace!("got item");
|
|
||||||
let pvk = self.pvk;
|
|
||||||
Box::pin(async move { item.verify_single(pvk) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::io::{self, Read};
|
||||||
|
|
||||||
|
use blake2b_simd::State;
|
||||||
|
|
||||||
|
/// Abstraction over a reader which hashes the data being read.
|
||||||
|
pub struct HashReader<R: Read> {
|
||||||
|
reader: R,
|
||||||
|
hasher: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> HashReader<R> {
|
||||||
|
/// Construct a new `HashReader` given an existing `reader` by value.
|
||||||
|
pub fn new(reader: R) -> Self {
|
||||||
|
HashReader {
|
||||||
|
reader,
|
||||||
|
hasher: State::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy this reader and return the hash of what was read.
|
||||||
|
pub fn into_hash(self) -> String {
|
||||||
|
let hash = self.hasher.finalize();
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
for c in hash.as_bytes().iter() {
|
||||||
|
s += &format!("{:02x}", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> Read for HashReader<R> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
let bytes = self.reader.read(buf)?;
|
||||||
|
|
||||||
|
if bytes > 0 {
|
||||||
|
self.hasher.update(&buf[0..bytes]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
use std::io::{self, BufReader};
|
||||||
|
|
||||||
|
use bellman::groth16;
|
||||||
|
use bls12_381::Bls12;
|
||||||
|
|
||||||
|
use super::HashReader;
|
||||||
|
|
||||||
|
const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c";
|
||||||
|
const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028";
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref PARAMS: Groth16Params = Groth16Params::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Groth16Params {
|
||||||
|
pub sapling: SaplingParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Groth16Params {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
sapling: SaplingParams::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct SaplingParams {
|
||||||
|
pub spend: groth16::Parameters<Bls12>,
|
||||||
|
pub spend_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
|
||||||
|
|
||||||
|
pub output: groth16::Parameters<Bls12>,
|
||||||
|
pub output_prepared_verifying_key: groth16::PreparedVerifyingKey<Bls12>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SaplingParams {
|
||||||
|
fn new() -> Self {
|
||||||
|
let (spend, output) = wagyu_zcash_parameters::load_sapling_parameters();
|
||||||
|
let spend_fs = BufReader::with_capacity(1024 * 1024, &spend[..]);
|
||||||
|
let output_fs = BufReader::with_capacity(1024 * 1024, &output[..]);
|
||||||
|
|
||||||
|
Self::read(spend_fs, output_fs)
|
||||||
|
.expect("reading parameters from wagyu zcash parameter's vec will always succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read<R: io::Read>(spend_fs: R, output_fs: R) -> Result<Self, io::Error> {
|
||||||
|
let mut spend_fs = HashReader::new(spend_fs);
|
||||||
|
let mut output_fs = HashReader::new(output_fs);
|
||||||
|
|
||||||
|
// Deserialize params
|
||||||
|
let spend = groth16::Parameters::<Bls12>::read(&mut spend_fs, false)?;
|
||||||
|
let output = groth16::Parameters::<Bls12>::read(&mut output_fs, false)?;
|
||||||
|
|
||||||
|
// There is extra stuff (the transcript) at the end of the parameter file which is
|
||||||
|
// used to verify the parameter validity, but we're not interested in that. We do
|
||||||
|
// want to read it, though, so that the BLAKE2b computed afterward is consistent
|
||||||
|
// with `b2sum` on the files.
|
||||||
|
let mut sink = io::sink();
|
||||||
|
io::copy(&mut spend_fs, &mut sink)?;
|
||||||
|
io::copy(&mut output_fs, &mut sink)?;
|
||||||
|
|
||||||
|
if spend_fs.into_hash() != SAPLING_SPEND_HASH {
|
||||||
|
panic!("Sapling spend parameter is not correct.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_fs.into_hash() != SAPLING_OUTPUT_HASH {
|
||||||
|
panic!("Sapling output parameter is not correct.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare verifying keys
|
||||||
|
let spend_prepared_verifying_key = groth16::prepare_verifying_key(&spend.vk);
|
||||||
|
let output_prepared_verifying_key = groth16::prepare_verifying_key(&output.vk);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
spend,
|
||||||
|
spend_prepared_verifying_key,
|
||||||
|
output,
|
||||||
|
output_prepared_verifying_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
//! Tests for transaction verification
|
||||||
|
|
||||||
|
use futures::stream::{FuturesUnordered, StreamExt};
|
||||||
|
use tower::ServiceExt;
|
||||||
|
|
||||||
|
use zebra_chain::{block::Block, serialization::ZcashDeserializeInto, transaction::Transaction};
|
||||||
|
|
||||||
|
use crate::primitives::groth16;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
async fn verify_groth16_spends_and_outputs<V>(
|
||||||
|
spend_verifier: &mut V,
|
||||||
|
output_verifier: &mut V,
|
||||||
|
transactions: Vec<std::sync::Arc<Transaction>>,
|
||||||
|
) -> Result<(), V::Error>
|
||||||
|
where
|
||||||
|
V: tower::Service<Item, Response = ()>,
|
||||||
|
<V as tower::Service<bellman::groth16::batch::Item<bls12_381::Bls12>>>::Error:
|
||||||
|
std::convert::From<
|
||||||
|
std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>,
|
||||||
|
>,
|
||||||
|
{
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
let mut async_checks = FuturesUnordered::new();
|
||||||
|
|
||||||
|
for tx in transactions {
|
||||||
|
match &*tx {
|
||||||
|
Transaction::V1 { .. }
|
||||||
|
| Transaction::V2 { .. }
|
||||||
|
| Transaction::V3 { .. }
|
||||||
|
| Transaction::V5 { .. } => (),
|
||||||
|
Transaction::V4 { shielded_data, .. } => {
|
||||||
|
if let Some(shielded_data) = shielded_data {
|
||||||
|
for spend in shielded_data.spends() {
|
||||||
|
tracing::trace!(?spend);
|
||||||
|
|
||||||
|
let spend_rsp = spend_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await?
|
||||||
|
.call(groth16::ItemWrapper::from(spend).into());
|
||||||
|
|
||||||
|
async_checks.push(spend_rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
for output in shielded_data.outputs() {
|
||||||
|
tracing::trace!(?output);
|
||||||
|
|
||||||
|
let output_rsp = output_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await?
|
||||||
|
.call(groth16::ItemWrapper::from(output).into());
|
||||||
|
|
||||||
|
async_checks.push(output_rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(result) = async_checks.next().await {
|
||||||
|
result?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn verify_sapling_groth16() {
|
||||||
|
// Since we expect these to pass, we can use the communal verifiers.
|
||||||
|
let mut spend_verifier = groth16::SPEND_VERIFIER.clone();
|
||||||
|
let mut output_verifier = groth16::OUTPUT_VERIFIER.clone();
|
||||||
|
|
||||||
|
let transactions = zebra_test::vectors::MAINNET_BLOCKS
|
||||||
|
.clone()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, bytes)| {
|
||||||
|
let block = bytes
|
||||||
|
.zcash_deserialize_into::<Block>()
|
||||||
|
.expect("a valid block");
|
||||||
|
block.transactions
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// This should fail if any of the proofs fail to validate.
|
||||||
|
verify_groth16_spends_and_outputs(&mut spend_verifier, &mut output_verifier, transactions)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn verify_invalid_groth16_output_description<V>(
|
||||||
|
output_verifier: &mut V,
|
||||||
|
transactions: Vec<std::sync::Arc<Transaction>>,
|
||||||
|
) -> Result<(), V::Error>
|
||||||
|
where
|
||||||
|
V: tower::Service<Item, Response = ()>,
|
||||||
|
<V as tower::Service<bellman::groth16::batch::Item<bls12_381::Bls12>>>::Error:
|
||||||
|
std::convert::From<
|
||||||
|
std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>,
|
||||||
|
>,
|
||||||
|
{
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
let mut async_checks = FuturesUnordered::new();
|
||||||
|
|
||||||
|
for tx in transactions {
|
||||||
|
match &*tx {
|
||||||
|
Transaction::V1 { .. }
|
||||||
|
| Transaction::V2 { .. }
|
||||||
|
| Transaction::V3 { .. }
|
||||||
|
| Transaction::V5 { .. } => (),
|
||||||
|
Transaction::V4 { shielded_data, .. } => {
|
||||||
|
if let Some(shielded_data) = shielded_data {
|
||||||
|
for output in shielded_data.outputs() {
|
||||||
|
let mut modified_output = output.clone();
|
||||||
|
modified_output.cm_u = jubjub::Fq::zero();
|
||||||
|
|
||||||
|
tracing::trace!(?modified_output);
|
||||||
|
|
||||||
|
let output_rsp = output_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await?
|
||||||
|
.call(groth16::ItemWrapper::from(&modified_output).into());
|
||||||
|
|
||||||
|
async_checks.push(output_rsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(result) = async_checks.next().await {
|
||||||
|
result?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[should_panic]
|
||||||
|
async fn correctly_err_on_invalid_output_proof() {
|
||||||
|
// Since we expect these to fail, we don't want to poison the communal
|
||||||
|
// verifiers.
|
||||||
|
let mut output_verifier = Fallback::new(
|
||||||
|
Batch::new(
|
||||||
|
Verifier::new(&PARAMS.sapling.output.vk),
|
||||||
|
crate::primitives::MAX_BATCH_SIZE,
|
||||||
|
crate::primitives::MAX_BATCH_LATENCY,
|
||||||
|
),
|
||||||
|
tower::service_fn(
|
||||||
|
(|item: Item| {
|
||||||
|
ready(item.verify_single(&prepare_verifying_key(&PARAMS.sapling.output.vk)))
|
||||||
|
}) as fn(_) -> _,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let block = zebra_test::vectors::BLOCK_MAINNET_903001_BYTES
|
||||||
|
.clone()
|
||||||
|
.zcash_deserialize_into::<Block>()
|
||||||
|
.expect("a valid block");
|
||||||
|
|
||||||
|
verify_invalid_groth16_output_description(&mut output_verifier, block.transactions)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ async fn sign_and_verify<V>(mut verifier: V, n: usize) -> Result<(), V::Error>
|
||||||
where
|
where
|
||||||
V: Service<Item, Response = ()>,
|
V: Service<Item, Response = ()>,
|
||||||
{
|
{
|
||||||
let rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let mut results = FuturesUnordered::new();
|
let mut results = FuturesUnordered::new();
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
let span = tracing::trace_span!("sig", i);
|
let span = tracing::trace_span!("sig", i);
|
||||||
|
@ -21,16 +21,16 @@ where
|
||||||
|
|
||||||
match i % 2 {
|
match i % 2 {
|
||||||
0 => {
|
0 => {
|
||||||
let sk = SigningKey::<SpendAuth>::new(rng);
|
let sk = SigningKey::<SpendAuth>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let sig = sk.sign(rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
verifier.ready_and().await?;
|
verifier.ready_and().await?;
|
||||||
results.push(span.in_scope(|| verifier.call((vk.into(), sig, msg).into())))
|
results.push(span.in_scope(|| verifier.call((vk.into(), sig, msg).into())))
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let sk = SigningKey::<Binding>::new(rng);
|
let sk = SigningKey::<Binding>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let sig = sk.sign(rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
verifier.ready_and().await?;
|
verifier.ready_and().await?;
|
||||||
results.push(span.in_scope(|| verifier.call((vk.into(), sig, msg).into())))
|
results.push(span.in_scope(|| verifier.call((vk.into(), sig, msg).into())))
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use zebra_chain::{
|
||||||
use zebra_script::CachedFfiTransaction;
|
use zebra_script::CachedFfiTransaction;
|
||||||
use zebra_state as zs;
|
use zebra_state as zs;
|
||||||
|
|
||||||
use crate::{error::TransactionError, script, BoxError};
|
use crate::{error::TransactionError, primitives, script, BoxError};
|
||||||
|
|
||||||
mod check;
|
mod check;
|
||||||
|
|
||||||
|
@ -126,9 +126,14 @@ where
|
||||||
} => (transaction, known_utxos, upgrade),
|
} => (transaction, known_utxos, upgrade),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut redjubjub_verifier = crate::primitives::redjubjub::VERIFIER.clone();
|
let mut spend_verifier = primitives::groth16::SPEND_VERIFIER.clone();
|
||||||
|
let mut output_verifier = primitives::groth16::OUTPUT_VERIFIER.clone();
|
||||||
|
|
||||||
|
let mut redjubjub_verifier = primitives::redjubjub::VERIFIER.clone();
|
||||||
let mut script_verifier = self.script_verifier.clone();
|
let mut script_verifier = self.script_verifier.clone();
|
||||||
|
|
||||||
let span = tracing::debug_span!("tx", hash = %tx.hash());
|
let span = tracing::debug_span!("tx", hash = %tx.hash());
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
tracing::trace!(?tx);
|
tracing::trace!(?tx);
|
||||||
match &*tx {
|
match &*tx {
|
||||||
|
@ -195,11 +200,35 @@ where
|
||||||
|
|
||||||
if let Some(shielded_data) = shielded_data {
|
if let Some(shielded_data) = shielded_data {
|
||||||
check::shielded_balances_match(&shielded_data, *value_balance)?;
|
check::shielded_balances_match(&shielded_data, *value_balance)?;
|
||||||
for spend in shielded_data.spends() {
|
|
||||||
// TODO: check that spend.cv and spend.rk are NOT of small
|
|
||||||
// order.
|
|
||||||
// https://zips.z.cash/protocol/protocol.pdf#spenddesc
|
|
||||||
|
|
||||||
|
for spend in shielded_data.spends() {
|
||||||
|
// Consensus rule: cv and rk MUST NOT be of small
|
||||||
|
// order, i.e. [h_J]cv MUST NOT be 𝒪_J and [h_J]rk
|
||||||
|
// MUST NOT be 𝒪_J.
|
||||||
|
//
|
||||||
|
// https://zips.z.cash/protocol/protocol.pdf#spenddesc
|
||||||
|
check::spend_cv_rk_not_small_order(spend)?;
|
||||||
|
|
||||||
|
// Consensus rule: The proof π_ZKSpend MUST be valid
|
||||||
|
// given a primary input formed from the other
|
||||||
|
// fields except spendAuthSig.
|
||||||
|
//
|
||||||
|
// Queue the verification of the Groth16 spend proof
|
||||||
|
// for each Spend description while adding the
|
||||||
|
// resulting future to our collection of async
|
||||||
|
// checks that (at a minimum) must pass for the
|
||||||
|
// transaction to verify.
|
||||||
|
let spend_rsp = spend_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await?
|
||||||
|
.call(primitives::groth16::ItemWrapper::from(spend).into());
|
||||||
|
|
||||||
|
async_checks.push(spend_rsp.boxed());
|
||||||
|
|
||||||
|
// Consensus rule: The spend authorization signature
|
||||||
|
// MUST be a valid SpendAuthSig signature over
|
||||||
|
// SigHash using rk as the validating key.
|
||||||
|
//
|
||||||
// Queue the validation of the RedJubjub spend
|
// Queue the validation of the RedJubjub spend
|
||||||
// authorization signature for each Spend
|
// authorization signature for each Spend
|
||||||
// description while adding the resulting future to
|
// description while adding the resulting future to
|
||||||
|
@ -211,32 +240,33 @@ where
|
||||||
.call((spend.rk, spend.spend_auth_sig, &sighash).into());
|
.call((spend.rk, spend.spend_auth_sig, &sighash).into());
|
||||||
|
|
||||||
// Disable pending sighash check #1377
|
// Disable pending sighash check #1377
|
||||||
//async_checks.push(rsp.boxed());
|
// async_checks.push(rsp.boxed());
|
||||||
|
|
||||||
// TODO: prepare public inputs for spends, then create
|
|
||||||
// a groth16::Item and pass to self.spend
|
|
||||||
|
|
||||||
// Queue the verification of the Groth16 spend proof
|
|
||||||
// for each Spend description while adding the
|
|
||||||
// resulting future to our collection of async
|
|
||||||
// checks that (at a minimum) must pass for the
|
|
||||||
// transaction to verify.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shielded_data.outputs().for_each(|_output| {
|
for output in shielded_data.outputs() {
|
||||||
// TODO: check that output.cv and output.epk are NOT of small
|
// Consensus rule: cv and wpk MUST NOT be of small
|
||||||
// order.
|
// order, i.e. [h_J]cv MUST NOT be 𝒪_J and [h_J]wpk
|
||||||
|
// MUST NOT be 𝒪_J.
|
||||||
|
//
|
||||||
// https://zips.z.cash/protocol/protocol.pdf#outputdesc
|
// https://zips.z.cash/protocol/protocol.pdf#outputdesc
|
||||||
|
check::output_cv_epk_not_small_order(output)?;
|
||||||
|
|
||||||
// TODO: prepare public inputs for outputs, then create
|
// Consensus rule: The proof π_ZKOutput MUST be
|
||||||
// a groth16::Item and pass to self.output
|
// valid given a primary input formed from the other
|
||||||
|
// fields except C^enc and C^out.
|
||||||
|
//
|
||||||
// Queue the verification of the Groth16 output
|
// Queue the verification of the Groth16 output
|
||||||
// proof for each Output description while adding
|
// proof for each Output description while adding
|
||||||
// the resulting future to our collection of async
|
// the resulting future to our collection of async
|
||||||
// checks that (at a minimum) must pass for the
|
// checks that (at a minimum) must pass for the
|
||||||
// transaction to verify.
|
// transaction to verify.
|
||||||
});
|
let output_rsp = output_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await?
|
||||||
|
.call(primitives::groth16::ItemWrapper::from(output).into());
|
||||||
|
|
||||||
|
async_checks.push(output_rsp.boxed());
|
||||||
|
}
|
||||||
|
|
||||||
let bvk = shielded_data.binding_verification_key(*value_balance);
|
let bvk = shielded_data.binding_verification_key(*value_balance);
|
||||||
let _rsp = redjubjub_verifier
|
let _rsp = redjubjub_verifier
|
||||||
|
@ -246,7 +276,7 @@ where
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
// Disable pending sighash check #1377
|
// Disable pending sighash check #1377
|
||||||
//async_checks.push(rsp);
|
// async_checks.push(rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, wait for all asynchronous checks to complete
|
// Finally, wait for all asynchronous checks to complete
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::convert::TryFrom;
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
amount::Amount,
|
amount::Amount,
|
||||||
primitives::{ed25519, Groth16Proof},
|
primitives::{ed25519, Groth16Proof},
|
||||||
|
sapling::{Output, Spend},
|
||||||
transaction::{JoinSplitData, ShieldedData, Transaction},
|
transaction::{JoinSplitData, ShieldedData, Transaction},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,3 +127,39 @@ pub fn coinbase_tx_no_joinsplit_or_spend(tx: &Transaction) -> Result<(), Transac
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that a Spend description's cv and rk are not of small order,
|
||||||
|
/// i.e. [h_J]cv MUST NOT be 𝒪_J and [h_J]rk MUST NOT be 𝒪_J.
|
||||||
|
///
|
||||||
|
/// https://zips.z.cash/protocol/protocol.pdf#spenddesc
|
||||||
|
pub fn spend_cv_rk_not_small_order(spend: &Spend) -> Result<(), TransactionError> {
|
||||||
|
if bool::from(spend.cv.0.is_small_order())
|
||||||
|
|| bool::from(
|
||||||
|
jubjub::AffinePoint::from_bytes(spend.rk.into())
|
||||||
|
.unwrap()
|
||||||
|
.is_small_order(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Err(TransactionError::SmallOrder)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check that a Output description's cv and epk are not of small order,
|
||||||
|
/// i.e. [h_J]cv MUST NOT be 𝒪_J and [h_J]epk MUST NOT be 𝒪_J.
|
||||||
|
///
|
||||||
|
/// https://zips.z.cash/protocol/protocol.pdf#outputdesc
|
||||||
|
pub fn output_cv_epk_not_small_order(output: &Output) -> Result<(), TransactionError> {
|
||||||
|
if bool::from(output.cv.0.is_small_order())
|
||||||
|
|| bool::from(
|
||||||
|
jubjub::AffinePoint::from_bytes(output.ephemeral_key.into())
|
||||||
|
.unwrap()
|
||||||
|
.is_small_order(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Err(TransactionError::SmallOrder)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ hex = "0.4"
|
||||||
indexmap = { version = "1.6", default-features = false }
|
indexmap = { version = "1.6", default-features = false }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
pin-project = "0.4"
|
pin-project = "0.4"
|
||||||
rand = "0.7"
|
rand = "0.8"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
serde = { version = "1", features = ["serde_derive"] }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
|
@ -20,7 +20,7 @@ gumdrop = "0.7"
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
serde = { version = "1", features = ["serde_derive"] }
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
rand = "0.7"
|
rand = "0.8"
|
||||||
|
|
||||||
hyper = { version = "0.14.0-dev", features = ["full"] }
|
hyper = { version = "0.14.0-dev", features = ["full"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|
|
@ -919,7 +919,9 @@ fn random_known_port() -> u16 {
|
||||||
// - macOS and Windows sequential ephemeral port allocations,
|
// - macOS and Windows sequential ephemeral port allocations,
|
||||||
// starting from 49152:
|
// starting from 49152:
|
||||||
// - https://dataplane.org/ephemeralports.html
|
// - https://dataplane.org/ephemeralports.html
|
||||||
rand::thread_rng().gen_range(53500, 60999)
|
use rand::Rng;
|
||||||
|
|
||||||
|
rand::thread_rng().gen_range(53500..60999)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the "magic" port number that tells the operating system to
|
/// Returns the "magic" port number that tells the operating system to
|
||||||
|
|
Loading…
Reference in New Issue