build(deps): update ecc dependencies for `zcashd` 5.6.0, and create legacy state format compatibility layer (#7053)
* update ecc dependencies no serialization * use zcash_primitives merke tree functions for serialization/deserialization in orchard and sapling trees * some more work in sapling/orchard serialization/deserialization * clippy * fix doc links * fix missing doc * make orchard trees serializa/deserialize as they were before upgrade * make sapling trees serialize/deserialize as they were before upgrade * use legacy for sprout * remove unused code * readd snapshot tests * repalce some code * upgrade zcash_proofs * remove legacy code for sprout * fix the count method * add root to serialize * fixes in as_bytes and from_bytes * use legacy code * add todo about pow2 tests * remove unused sprout code * fix doc typos * Add a recalculate_root() method to trees for tests * Rename test tree types to make them easier to change * Add TODOs for tests for old and new serialization formats * fix doc typos * add more test to note commitment trees * fix comment * fix leaf serializatiuon, reverse to old hashes in pow2 tests * fix serialization * put sapling SerializedTree code back * put orchard SerializedTree code back * clippy * add duplicated dependencies until zebra_script updates * fix a doc link * minor cleanup * remove todo comment from tests * add one more check to tests * update zebra_script * update deny.toml * replace custom function with library * fix some tests * update docs * Remove duplicate dependencies from deny.toml Based on the list at: https://github.com/ZcashFoundation/zebra/actions/runs/5557139662/jobs/10150543673?pr=7053#step:5:14 --------- Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
parent
3faef29d23
commit
e2f010eb4f
199
Cargo.lock
199
Cargo.lock
|
@ -77,17 +77,6 @@ dependencies = [
|
|||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.10",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
|
@ -550,12 +539,12 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.4.0"
|
||||
name = "bridgetree"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
checksum = "3a813dadc684e4c78a4547757debd99666282545d90e4ccc3210913ed4337ad2"
|
||||
dependencies = [
|
||||
"sha2 0.9.9",
|
||||
"incrementalmerkletree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1072,9 +1061,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
|
||||
checksum = "109308c20e8445959c2792e81871054c6a17e6976489a93d2769641a2ba5839c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
|
@ -1096,15 +1085,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
|
||||
checksum = "882074421238e84fe3b4c65d0081de34e5b323bf64555d3e61991f76eb64a7bb"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||
checksum = "4a076022ece33e7686fb76513518e219cca4fce5750a8ae6d1ce6c0f48fd1af9"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.63",
|
||||
"quote 1.0.29",
|
||||
|
@ -1211,15 +1200,6 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
|
@ -1764,7 +1744,7 @@ version = "0.13.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1773,7 +1753,7 @@ version = "0.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
|
@ -1792,14 +1772,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hdwallet"
|
||||
version = "0.3.1"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cd89bf343be18dbe1e505100e48168bbd084760e842a8fed0317d2361470193"
|
||||
checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rand_core 0.6.4",
|
||||
"ring",
|
||||
"secp256k1",
|
||||
"secp256k1 0.26.0",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1850,6 +1831,15 @@ dependencies = [
|
|||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
|
@ -2045,11 +2035,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "incrementalmerkletree"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b"
|
||||
checksum = "2eb91780c91bfc79769006a55c49127b83e1c1a6cf2b3b149ce3f247cbe342f0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2089,7 +2079,7 @@ dependencies = [
|
|||
"console",
|
||||
"instant",
|
||||
"number_prefix",
|
||||
"portable-atomic 1.3.3",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
|
@ -2099,7 +2089,7 @@ version = "0.11.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"ahash",
|
||||
"is-terminal",
|
||||
"itoa",
|
||||
"log",
|
||||
|
@ -2291,6 +2281,15 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "known-folders"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b6f1427d9c43b1cce87434c4d9eca33f43bdbb6246a762aa823a582f74c1684"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -2478,26 +2477,15 @@ dependencies = [
|
|||
"nonempty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metrics"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b9b8653cec6897f73b519a43fba5ee3d50f62fe9af80b428accdcc093b4a849"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
"metrics-macros 0.6.0",
|
||||
"portable-atomic 0.3.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metrics"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"metrics-macros 0.7.0",
|
||||
"portable-atomic 1.3.3",
|
||||
"ahash",
|
||||
"metrics-macros",
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2510,24 +2498,13 @@ dependencies = [
|
|||
"hyper",
|
||||
"indexmap 1.9.3",
|
||||
"ipnet",
|
||||
"metrics 0.21.1",
|
||||
"metrics",
|
||||
"metrics-util",
|
||||
"quanta",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metrics-macros"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "731f8ecebd9f3a4aa847dfe75455e4757a45da40a7793d2f0b1f9b6ed18b23f3"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.63",
|
||||
"quote 1.0.29",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metrics-macros"
|
||||
version = "0.7.0"
|
||||
|
@ -2548,7 +2525,7 @@ dependencies = [
|
|||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"hashbrown 0.13.2",
|
||||
"metrics 0.21.1",
|
||||
"metrics",
|
||||
"num_cpus",
|
||||
"quanta",
|
||||
"sketches-ddsketch",
|
||||
|
@ -2829,9 +2806,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
|||
|
||||
[[package]]
|
||||
name = "orchard"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6f418f2c25573923f81a091f38b4b19bc20f6c92b5070fb8f0711e64a2b998"
|
||||
checksum = "5f4e7a52f510cb8c39e639e662a353adbaf86025478af89ae54a0551f8ca35e2"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bitvec",
|
||||
|
@ -3156,15 +3133,6 @@ dependencies = [
|
|||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e"
|
||||
dependencies = [
|
||||
"portable-atomic 1.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.3.3"
|
||||
|
@ -3911,10 +3879,19 @@ version = "0.21.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260"
|
||||
dependencies = [
|
||||
"secp256k1-sys",
|
||||
"secp256k1-sys 0.4.2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894"
|
||||
dependencies = [
|
||||
"secp256k1-sys 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1-sys"
|
||||
version = "0.4.2"
|
||||
|
@ -3924,6 +3901,15 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1-sys"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secrecy"
|
||||
version = "0.8.0"
|
||||
|
@ -5538,6 +5524,15 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee"
|
||||
dependencies = [
|
||||
"home",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
@ -5549,12 +5544,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zcash_address"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52be35a205369d480378646bff9c9fedafd8efe8af1e0e54bb858f405883f2b2"
|
||||
checksum = "8944af5c206cf2e37020ad54618e1825501b98548d35a638b73e0ec5762df8d5"
|
||||
dependencies = [
|
||||
"bech32",
|
||||
"bs58 0.4.0",
|
||||
"bs58",
|
||||
"f4jumble",
|
||||
"zcash_encoding",
|
||||
]
|
||||
|
@ -5582,9 +5577,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zcash_note_encryption"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb2149e6cd5fbee36c5b87c601715a8c35554602f7fe84af38b636afa2db318"
|
||||
checksum = "5b4580cd6cee12e44421dac43169be8d23791650816bdb34e6ddfa70ac89c1c5"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
|
@ -5595,9 +5590,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zcash_primitives"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914d2195a478d5b63191584dff126f552751115181857b290211ec88e68acc3e"
|
||||
checksum = "de1a231e6a58d3dcdd6e21d229db33d7c10f9b54d8c170e122b267f6826bb48f"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bip0039",
|
||||
|
@ -5621,7 +5616,7 @@ dependencies = [
|
|||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"ripemd",
|
||||
"secp256k1",
|
||||
"secp256k1 0.26.0",
|
||||
"sha2 0.10.6",
|
||||
"subtle",
|
||||
"zcash_address",
|
||||
|
@ -5631,34 +5626,38 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zcash_proofs"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5c8147884952748b00aa443d36511ae2d7b49acfec74cfd39c0959fbb61ef14"
|
||||
checksum = "59d2e066a717f28451a081f2ebd483ddda896cf00d572972c10979d645ffa6c4"
|
||||
dependencies = [
|
||||
"bellman",
|
||||
"blake2b_simd",
|
||||
"bls12_381",
|
||||
"directories",
|
||||
"group",
|
||||
"home",
|
||||
"incrementalmerkletree",
|
||||
"jubjub",
|
||||
"known-folders",
|
||||
"lazy_static",
|
||||
"minreq",
|
||||
"rand_core 0.6.4",
|
||||
"redjubjub",
|
||||
"tracing",
|
||||
"xdg",
|
||||
"zcash_primitives",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zcash_script"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f5d794b254efc2759d249b477f53faa751f67543a4b4d1c7a5ff7df212d4ba5"
|
||||
checksum = "8c4f95043fd34d402b8d5debb0e54a28c2b84fc99591f5973b4999e9c5b01bfd"
|
||||
dependencies = [
|
||||
"bellman",
|
||||
"bindgen",
|
||||
"blake2b_simd",
|
||||
"bls12_381",
|
||||
"bridgetree",
|
||||
"byteorder",
|
||||
"cc",
|
||||
"crossbeam-channel",
|
||||
|
@ -5669,8 +5668,9 @@ dependencies = [
|
|||
"jubjub",
|
||||
"libc",
|
||||
"memuse",
|
||||
"metrics 0.20.1",
|
||||
"metrics",
|
||||
"orchard",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"rayon",
|
||||
"subtle",
|
||||
|
@ -5692,7 +5692,8 @@ dependencies = [
|
|||
"bitvec",
|
||||
"blake2b_simd",
|
||||
"blake2s_simd",
|
||||
"bs58 0.5.0",
|
||||
"bridgetree",
|
||||
"bs58",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"color-eyre",
|
||||
|
@ -5721,7 +5722,7 @@ dependencies = [
|
|||
"reddsa",
|
||||
"redjubjub",
|
||||
"ripemd",
|
||||
"secp256k1",
|
||||
"secp256k1 0.21.3",
|
||||
"serde",
|
||||
"serde-big-array",
|
||||
"serde_json",
|
||||
|
@ -5760,7 +5761,7 @@ dependencies = [
|
|||
"howudoin",
|
||||
"jubjub",
|
||||
"lazy_static",
|
||||
"metrics 0.21.1",
|
||||
"metrics",
|
||||
"num-integer",
|
||||
"once_cell",
|
||||
"orchard",
|
||||
|
@ -5804,7 +5805,7 @@ dependencies = [
|
|||
"indexmap 2.0.0",
|
||||
"itertools 0.11.0",
|
||||
"lazy_static",
|
||||
"metrics 0.21.1",
|
||||
"metrics",
|
||||
"num-integer",
|
||||
"ordered-map",
|
||||
"pin-project",
|
||||
|
@ -5904,7 +5905,7 @@ dependencies = [
|
|||
"itertools 0.11.0",
|
||||
"jubjub",
|
||||
"lazy_static",
|
||||
"metrics 0.21.1",
|
||||
"metrics",
|
||||
"mset",
|
||||
"once_cell",
|
||||
"proptest",
|
||||
|
@ -5998,7 +5999,7 @@ dependencies = [
|
|||
"jsonrpc-core",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"metrics 0.21.1",
|
||||
"metrics",
|
||||
"metrics-exporter-prometheus",
|
||||
"num-integer",
|
||||
"once_cell",
|
||||
|
|
14
deny.toml
14
deny.toml
|
@ -70,26 +70,24 @@ skip-tree = [
|
|||
|
||||
# wait for zcashd and zcash_script to upgrade
|
||||
# https://github.com/ZcashFoundation/zcash_script/pulls
|
||||
{ name = "metrics", version = "=0.20.1" },
|
||||
{ name = "sha2", version = "=0.9.9" },
|
||||
|
||||
# wait for ed25519-zebra, indexmap, metrics-util, and metrics to upgrade
|
||||
# ed25519-zebra/hashbrown: https://github.com/ZcashFoundation/ed25519-zebra/pull/65
|
||||
{ name = "ahash", version = "=0.7.6" },
|
||||
|
||||
# wait for indexmap, toml_edit, serde_json, tower to upgrade
|
||||
{ name = "hashbrown", version = "=0.12.3" },
|
||||
# wait for metrics-exporter-prometheus to upgrade
|
||||
{ name = "hashbrown", version = "=0.13.2" },
|
||||
|
||||
# wait for zebra-chain to upgrade
|
||||
{ name = "secp256k1", version = "=0.21.3" },
|
||||
|
||||
# wait for zebra-chain to upgrade `secp256k1`
|
||||
{ name = "secp256k1-sys", version = "=0.4.2" },
|
||||
|
||||
# ECC crates
|
||||
|
||||
# wait for zcash_primitives to remove duplicated dependencies
|
||||
{ name = "block-buffer", version = "=0.9.0" },
|
||||
|
||||
# wait for zcash_address to upgrade
|
||||
{ name = "bs58", version = "=0.4.0" },
|
||||
|
||||
# wait for minreq and zcash_proofs to upgrade
|
||||
{ name = "rustls", version = "=0.20.8" },
|
||||
|
||||
|
|
|
@ -53,11 +53,12 @@ bitflags = "2.3.3"
|
|||
bitflags-serde-legacy = "0.1.1"
|
||||
blake2b_simd = "1.0.1"
|
||||
blake2s_simd = "1.0.1"
|
||||
bridgetree = "0.3.0"
|
||||
bs58 = { version = "0.5.0", features = ["check"] }
|
||||
byteorder = "1.4.3"
|
||||
equihash = "0.2.0"
|
||||
group = "0.13.0"
|
||||
incrementalmerkletree = "0.3.1"
|
||||
incrementalmerkletree = "0.4.0"
|
||||
jubjub = "0.10.0"
|
||||
lazy_static = "1.4.0"
|
||||
num-integer = "0.1.45"
|
||||
|
@ -72,11 +73,11 @@ x25519-dalek = { version = "2.0.0-rc.3", features = ["serde"] }
|
|||
|
||||
# ECC deps
|
||||
halo2 = { package = "halo2_proofs", version = "0.3.0" }
|
||||
orchard = "0.4.0"
|
||||
orchard = "0.5.0"
|
||||
zcash_encoding = "0.2.0"
|
||||
zcash_history = "0.3.0"
|
||||
zcash_note_encryption = "0.3.0"
|
||||
zcash_primitives = { version = "0.11.0", features = ["transparent-inputs"] }
|
||||
zcash_note_encryption = "0.4.0"
|
||||
zcash_primitives = { version = "0.12.0", features = ["transparent-inputs"] }
|
||||
|
||||
# Time
|
||||
chrono = { version = "0.4.26", default-features = false, features = ["clock", "std", "serde"] }
|
||||
|
@ -108,7 +109,7 @@ reddsa = "0.5.0"
|
|||
serde_json = { version = "1.0.100", optional = true }
|
||||
|
||||
# Experimental feature getblocktemplate-rpcs
|
||||
zcash_address = { version = "0.2.1", optional = true }
|
||||
zcash_address = { version = "0.3.0", optional = true }
|
||||
|
||||
# Optional testing dependencies
|
||||
proptest = { version = "1.2.0", optional = true }
|
||||
|
|
|
@ -18,11 +18,12 @@ use std::{
|
|||
};
|
||||
|
||||
use bitvec::prelude::*;
|
||||
use bridgetree;
|
||||
use halo2::pasta::{group::ff::PrimeField, pallas};
|
||||
use incrementalmerkletree::{bridgetree, Frontier};
|
||||
use incrementalmerkletree::Hashable;
|
||||
use lazy_static::lazy_static;
|
||||
use thiserror::Error;
|
||||
use zcash_primitives::merkle_tree::{self, CommitmentTree};
|
||||
use zcash_primitives::merkle_tree::{write_commitment_tree, HashSer};
|
||||
|
||||
use super::sinsemilla::*;
|
||||
|
||||
|
@ -30,6 +31,9 @@ use crate::serialization::{
|
|||
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
||||
};
|
||||
|
||||
pub mod legacy;
|
||||
use legacy::LegacyNoteCommitmentTree;
|
||||
|
||||
/// The type that is used to update the note commitment tree.
|
||||
///
|
||||
/// Unfortunately, this is not the same as `orchard::NoteCommitment`.
|
||||
|
@ -164,18 +168,18 @@ impl ZcashDeserialize for Root {
|
|||
|
||||
/// A node of the Orchard Incremental Note Commitment Tree.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
struct Node(pallas::Base);
|
||||
pub struct Node(pallas::Base);
|
||||
|
||||
/// Required to convert [`NoteCommitmentTree`] into [`SerializedTree`].
|
||||
///
|
||||
/// Zebra stores Orchard note commitment trees as [`Frontier`][1]s while the
|
||||
/// [`z_gettreestate`][2] RPC requires [`CommitmentTree`][3]s. Implementing
|
||||
/// [`merkle_tree::Hashable`] for [`Node`]s allows the conversion.
|
||||
/// [`HashSer`] for [`Node`]s allows the conversion.
|
||||
///
|
||||
/// [1]: bridgetree::Frontier
|
||||
/// [2]: https://zcash.github.io/rpc/z_gettreestate.html
|
||||
/// [3]: merkle_tree::CommitmentTree
|
||||
impl merkle_tree::Hashable for Node {
|
||||
/// [3]: incrementalmerkletree::frontier::CommitmentTree
|
||||
impl HashSer for Node {
|
||||
fn read<R: io::Read>(mut reader: R) -> io::Result<Self> {
|
||||
let mut repr = [0u8; 32];
|
||||
reader.read_exact(&mut repr)?;
|
||||
|
@ -192,24 +196,9 @@ impl merkle_tree::Hashable for Node {
|
|||
fn write<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.0.to_repr())
|
||||
}
|
||||
|
||||
fn combine(level: usize, a: &Self, b: &Self) -> Self {
|
||||
let level = u8::try_from(level).expect("level must fit into u8");
|
||||
let layer = MERKLE_DEPTH - 1 - level;
|
||||
Self(merkle_crh_orchard(layer, a.0, b.0))
|
||||
}
|
||||
|
||||
fn blank() -> Self {
|
||||
Self(NoteCommitmentTree::uncommitted())
|
||||
}
|
||||
|
||||
fn empty_root(level: usize) -> Self {
|
||||
let layer_below = usize::from(MERKLE_DEPTH) - level;
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
}
|
||||
|
||||
impl incrementalmerkletree::Hashable for Node {
|
||||
impl Hashable for Node {
|
||||
fn empty_leaf() -> Self {
|
||||
Self(NoteCommitmentTree::uncommitted())
|
||||
}
|
||||
|
@ -217,13 +206,13 @@ impl incrementalmerkletree::Hashable for Node {
|
|||
/// Combine two nodes to generate a new node in the given level.
|
||||
/// Level 0 is the layer above the leaves (layer 31).
|
||||
/// Level 31 is the root (layer 0).
|
||||
fn combine(level: incrementalmerkletree::Altitude, a: &Self, b: &Self) -> Self {
|
||||
fn combine(level: incrementalmerkletree::Level, a: &Self, b: &Self) -> Self {
|
||||
let layer = MERKLE_DEPTH - 1 - u8::from(level);
|
||||
Self(merkle_crh_orchard(layer, a.0, b.0))
|
||||
}
|
||||
|
||||
/// Return the node for the level below the given level. (A quirk of the API)
|
||||
fn empty_root(level: incrementalmerkletree::Altitude) -> Self {
|
||||
fn empty_root(level: incrementalmerkletree::Level) -> Self {
|
||||
let layer_below = usize::from(MERKLE_DEPTH) - usize::from(level);
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
|
@ -265,6 +254,8 @@ pub enum NoteCommitmentTreeError {
|
|||
|
||||
/// Orchard Incremental Note Commitment Tree
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(into = "LegacyNoteCommitmentTree")]
|
||||
#[serde(from = "LegacyNoteCommitmentTree")]
|
||||
pub struct NoteCommitmentTree {
|
||||
/// The tree represented as a Frontier.
|
||||
///
|
||||
|
@ -311,7 +302,7 @@ impl NoteCommitmentTree {
|
|||
/// Returns an error if the tree is full.
|
||||
#[allow(clippy::unwrap_in_result)]
|
||||
pub fn append(&mut self, cm_x: NoteCommitmentUpdate) -> Result<(), NoteCommitmentTreeError> {
|
||||
if self.inner.append(&cm_x.into()) {
|
||||
if self.inner.append(cm_x.into()) {
|
||||
// Invalidate cached root
|
||||
let cached_root = self
|
||||
.cached_root
|
||||
|
@ -385,7 +376,9 @@ impl NoteCommitmentTree {
|
|||
///
|
||||
/// For Orchard, the tree is capped at 2^32.
|
||||
pub fn count(&self) -> u64 {
|
||||
self.inner.position().map_or(0, |pos| u64::from(pos) + 1)
|
||||
self.inner
|
||||
.value()
|
||||
.map_or(0, |x| u64::from(x.position()) + 1)
|
||||
}
|
||||
|
||||
/// Checks if the tree roots and inner data structures of `self` and `other` are equal.
|
||||
|
@ -459,7 +452,7 @@ impl From<Vec<pallas::Base>> for NoteCommitmentTree {
|
|||
/// A serialized Orchard note commitment tree.
|
||||
///
|
||||
/// The format of the serialized data is compatible with
|
||||
/// [`CommitmentTree`](merkle_tree::CommitmentTree) from `librustzcash` and not
|
||||
/// [`CommitmentTree`](incrementalmerkletree::frontier::CommitmentTree) from `librustzcash` and not
|
||||
/// with [`Frontier`](bridgetree::Frontier) from the crate
|
||||
/// [`incrementalmerkletree`]. Zebra follows the former format in order to stay
|
||||
/// consistent with `zcashd` in RPCs. Note that [`NoteCommitmentTree`] itself is
|
||||
|
@ -468,7 +461,7 @@ impl From<Vec<pallas::Base>> for NoteCommitmentTree {
|
|||
/// The formats are semantically equivalent. The primary difference between them
|
||||
/// is that in [`Frontier`](bridgetree::Frontier), the vector of parents is
|
||||
/// dense (we know where the gaps are from the position of the leaf in the
|
||||
/// overall tree); whereas in [`CommitmentTree`](merkle_tree::CommitmentTree),
|
||||
/// overall tree); whereas in [`CommitmentTree`](incrementalmerkletree::frontier::CommitmentTree),
|
||||
/// the vector of parent hashes is sparse with [`None`] values in the gaps.
|
||||
///
|
||||
/// The sparse format, used in this implementation, allows representing invalid
|
||||
|
@ -498,8 +491,9 @@ impl From<&NoteCommitmentTree> for SerializedTree {
|
|||
// Convert the note commitment tree from
|
||||
// [`Frontier`](bridgetree::Frontier) to
|
||||
// [`CommitmentTree`](merkle_tree::CommitmentTree).
|
||||
let tree = CommitmentTree::from_frontier(&tree.inner);
|
||||
tree.write(&mut serialized_tree)
|
||||
let tree = incrementalmerkletree::frontier::CommitmentTree::from_frontier(&tree.inner);
|
||||
|
||||
write_commitment_tree(&tree, &mut serialized_tree)
|
||||
.expect("note commitment tree should be serializable");
|
||||
Self(serialized_tree)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
//! Orchard serialization legacy code.
|
||||
//!
|
||||
//! We create a [`LegacyNoteCommitmentTree`] which is a copy of [`NoteCommitmentTree`] but where serialization and
|
||||
//! deserialization can be derived.
|
||||
//! To do this we create a [`LegacyFrontier`] which is a legacy `Frontier` structure that can be found in [1],
|
||||
//! In order to make [`LegacyFrontier`] serializable we also have our own versions of `NonEmptyFrontier` ([`LegacyNonEmptyFrontier`]),
|
||||
//! `Leaf`([`LegacyLeaf`]) and `Position`([`LegacyPosition`]) that can be found in [1] or [2].
|
||||
//!
|
||||
//! Conversions methods to/from [`LegacyNoteCommitmentTree`] to/from [`NoteCommitmentTree`] are defined also in this file.
|
||||
//!
|
||||
//! [1]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/bridgetree.rs
|
||||
//! [2]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/lib.rs
|
||||
|
||||
use incrementalmerkletree::{frontier::Frontier, Position};
|
||||
|
||||
use super::{Node, NoteCommitmentTree, Root, MERKLE_DEPTH};
|
||||
|
||||
/// A legacy version of [`NoteCommitmentTree`].
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename = "NoteCommitmentTree")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyNoteCommitmentTree {
|
||||
pub inner: LegacyFrontier<Node, MERKLE_DEPTH>,
|
||||
cached_root: std::sync::RwLock<Option<Root>>,
|
||||
}
|
||||
|
||||
impl From<NoteCommitmentTree> for LegacyNoteCommitmentTree {
|
||||
fn from(nct: NoteCommitmentTree) -> Self {
|
||||
LegacyNoteCommitmentTree {
|
||||
inner: nct.inner.into(),
|
||||
cached_root: nct.cached_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyNoteCommitmentTree> for NoteCommitmentTree {
|
||||
fn from(legacy_nct: LegacyNoteCommitmentTree) -> Self {
|
||||
NoteCommitmentTree {
|
||||
inner: legacy_nct.inner.into(),
|
||||
cached_root: legacy_nct.cached_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Frontier")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyFrontier<H, const DEPTH: u8> {
|
||||
frontier: Option<LegacyNonEmptyFrontier<H>>,
|
||||
}
|
||||
|
||||
impl From<LegacyFrontier<Node, MERKLE_DEPTH>> for Frontier<Node, MERKLE_DEPTH> {
|
||||
fn from(legacy_frontier: LegacyFrontier<Node, MERKLE_DEPTH>) -> Self {
|
||||
if let Some(legacy_frontier_data) = legacy_frontier.frontier {
|
||||
let mut ommers = legacy_frontier_data.ommers;
|
||||
let position = Position::from(
|
||||
u64::try_from(legacy_frontier_data.position.0)
|
||||
.expect("old `usize` always fits in `u64`"),
|
||||
);
|
||||
let leaf = match legacy_frontier_data.leaf {
|
||||
LegacyLeaf::Left(a) => a,
|
||||
LegacyLeaf::Right(a, b) => {
|
||||
ommers.insert(0, a);
|
||||
b
|
||||
}
|
||||
};
|
||||
Frontier::from_parts(
|
||||
position,
|
||||
leaf,
|
||||
ommers,
|
||||
)
|
||||
.expect("We should be able to construct a frontier from parts given legacy frontier is not empty")
|
||||
} else {
|
||||
Frontier::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frontier<Node, MERKLE_DEPTH>> for LegacyFrontier<Node, MERKLE_DEPTH> {
|
||||
fn from(frontier: Frontier<Node, MERKLE_DEPTH>) -> Self {
|
||||
if let Some(frontier_data) = frontier.value() {
|
||||
let leaf_from_frontier = *frontier_data.leaf();
|
||||
let mut leaf = LegacyLeaf::Left(leaf_from_frontier);
|
||||
let mut ommers = frontier_data.ommers().to_vec();
|
||||
let position = usize::try_from(u64::from(frontier_data.position()))
|
||||
.expect("new position should fit in a `usize`");
|
||||
if frontier_data.position().is_odd() {
|
||||
let left = ommers.remove(0);
|
||||
leaf = LegacyLeaf::Right(left, leaf_from_frontier);
|
||||
}
|
||||
LegacyFrontier {
|
||||
frontier: Some(LegacyNonEmptyFrontier {
|
||||
position: LegacyPosition(position),
|
||||
leaf,
|
||||
ommers: ommers.to_vec(),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
LegacyFrontier { frontier: None }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "NonEmptyFrontier")]
|
||||
struct LegacyNonEmptyFrontier<H> {
|
||||
position: LegacyPosition,
|
||||
leaf: LegacyLeaf<H>,
|
||||
ommers: Vec<H>,
|
||||
}
|
||||
|
||||
/// A set of leaves of a Merkle tree.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Leaf")]
|
||||
enum LegacyLeaf<A> {
|
||||
Left(A),
|
||||
Right(A, A),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
struct LegacyPosition(usize);
|
|
@ -18,14 +18,13 @@ use std::{
|
|||
};
|
||||
|
||||
use bitvec::prelude::*;
|
||||
use incrementalmerkletree::{
|
||||
bridgetree::{self, Leaf},
|
||||
Frontier,
|
||||
};
|
||||
use bridgetree::{self};
|
||||
use incrementalmerkletree::{frontier::Frontier, Hashable};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use thiserror::Error;
|
||||
use zcash_encoding::{Optional, Vector};
|
||||
use zcash_primitives::merkle_tree::{self, Hashable};
|
||||
use zcash_primitives::merkle_tree::HashSer;
|
||||
|
||||
use super::commitment::pedersen_hashes::pedersen_hash;
|
||||
|
||||
|
@ -33,6 +32,9 @@ use crate::serialization::{
|
|||
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
||||
};
|
||||
|
||||
pub mod legacy;
|
||||
use legacy::{LegacyLeaf, LegacyNoteCommitmentTree};
|
||||
|
||||
/// The type that is used to update the note commitment tree.
|
||||
///
|
||||
/// Unfortunately, this is not the same as `sapling::NoteCommitment`.
|
||||
|
@ -85,12 +87,6 @@ lazy_static! {
|
|||
};
|
||||
}
|
||||
|
||||
/// The index of a note's commitment at the leafmost layer of its Note
|
||||
/// Commitment Tree.
|
||||
///
|
||||
/// <https://zips.z.cash/protocol/protocol.pdf#merkletree>
|
||||
pub struct Position(pub(crate) u64);
|
||||
|
||||
/// Sapling note commitment tree root node hash.
|
||||
///
|
||||
/// The root hash in LEBS2OSP256(rt) encoding of the Sapling note
|
||||
|
@ -167,7 +163,7 @@ impl ZcashDeserialize for Root {
|
|||
/// Note that it's handled as a byte buffer and not a point coordinate (jubjub::Fq)
|
||||
/// because that's how the spec handles the MerkleCRH^Sapling function inputs and outputs.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
struct Node([u8; 32]);
|
||||
pub struct Node([u8; 32]);
|
||||
|
||||
impl fmt::Debug for Node {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -179,12 +175,12 @@ impl fmt::Debug for Node {
|
|||
///
|
||||
/// Zebra stores Sapling note commitment trees as [`Frontier`][1]s while the
|
||||
/// [`z_gettreestate`][2] RPC requires [`CommitmentTree`][3]s. Implementing
|
||||
/// [`merkle_tree::Hashable`] for [`Node`]s allows the conversion.
|
||||
/// [`incrementalmerkletree::Hashable`] for [`Node`]s allows the conversion.
|
||||
///
|
||||
/// [1]: bridgetree::Frontier
|
||||
/// [2]: https://zcash.github.io/rpc/z_gettreestate.html
|
||||
/// [3]: merkle_tree::CommitmentTree
|
||||
impl merkle_tree::Hashable for Node {
|
||||
/// [3]: incrementalmerkletree::frontier::CommitmentTree
|
||||
impl HashSer for Node {
|
||||
fn read<R: io::Read>(mut reader: R) -> io::Result<Self> {
|
||||
let mut node = [0u8; 32];
|
||||
reader.read_exact(&mut node)?;
|
||||
|
@ -194,24 +190,9 @@ impl merkle_tree::Hashable for Node {
|
|||
fn write<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(self.0.as_ref())
|
||||
}
|
||||
|
||||
fn combine(level: usize, a: &Self, b: &Self) -> Self {
|
||||
let level = u8::try_from(level).expect("level must fit into u8");
|
||||
let layer = MERKLE_DEPTH - 1 - level;
|
||||
Self(merkle_crh_sapling(layer, a.0, b.0))
|
||||
}
|
||||
|
||||
fn blank() -> Self {
|
||||
Self(NoteCommitmentTree::uncommitted())
|
||||
}
|
||||
|
||||
fn empty_root(level: usize) -> Self {
|
||||
let layer_below = usize::from(MERKLE_DEPTH) - level;
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
}
|
||||
|
||||
impl incrementalmerkletree::Hashable for Node {
|
||||
impl Hashable for Node {
|
||||
fn empty_leaf() -> Self {
|
||||
Self(NoteCommitmentTree::uncommitted())
|
||||
}
|
||||
|
@ -219,13 +200,13 @@ impl incrementalmerkletree::Hashable for Node {
|
|||
/// Combine two nodes to generate a new node in the given level.
|
||||
/// Level 0 is the layer above the leaves (layer 31).
|
||||
/// Level 31 is the root (layer 0).
|
||||
fn combine(level: incrementalmerkletree::Altitude, a: &Self, b: &Self) -> Self {
|
||||
fn combine(level: incrementalmerkletree::Level, a: &Self, b: &Self) -> Self {
|
||||
let layer = MERKLE_DEPTH - 1 - u8::from(level);
|
||||
Self(merkle_crh_sapling(layer, a.0, b.0))
|
||||
}
|
||||
|
||||
/// Return the node for the level below the given level. (A quirk of the API)
|
||||
fn empty_root(level: incrementalmerkletree::Altitude) -> Self {
|
||||
fn empty_root(level: incrementalmerkletree::Level) -> Self {
|
||||
let layer_below = usize::from(MERKLE_DEPTH) - usize::from(level);
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
|
@ -267,6 +248,8 @@ pub enum NoteCommitmentTreeError {
|
|||
|
||||
/// Sapling Incremental Note Commitment Tree.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(into = "LegacyNoteCommitmentTree")]
|
||||
#[serde(from = "LegacyNoteCommitmentTree")]
|
||||
pub struct NoteCommitmentTree {
|
||||
/// The tree represented as a [`Frontier`](bridgetree::Frontier).
|
||||
///
|
||||
|
@ -284,7 +267,7 @@ pub struct NoteCommitmentTree {
|
|||
/// <https://zips.z.cash/protocol/protocol.pdf#merkletree>
|
||||
///
|
||||
/// Note: MerkleDepth^Sapling = MERKLE_DEPTH = 32.
|
||||
inner: bridgetree::Frontier<Node, MERKLE_DEPTH>,
|
||||
inner: Frontier<Node, MERKLE_DEPTH>,
|
||||
|
||||
/// A cached root of the tree.
|
||||
///
|
||||
|
@ -314,7 +297,7 @@ impl NoteCommitmentTree {
|
|||
/// Returns an error if the tree is full.
|
||||
#[allow(clippy::unwrap_in_result)]
|
||||
pub fn append(&mut self, cm_u: NoteCommitmentUpdate) -> Result<(), NoteCommitmentTreeError> {
|
||||
if self.inner.append(&cm_u.into()) {
|
||||
if self.inner.append(cm_u.into()) {
|
||||
// Invalidate cached root
|
||||
let cached_root = self
|
||||
.cached_root
|
||||
|
@ -388,7 +371,9 @@ impl NoteCommitmentTree {
|
|||
///
|
||||
/// For Sapling, the tree is capped at 2^32.
|
||||
pub fn count(&self) -> u64 {
|
||||
self.inner.position().map_or(0, |pos| u64::from(pos) + 1)
|
||||
self.inner
|
||||
.value()
|
||||
.map_or(0, |x| u64::from(x.position()) + 1)
|
||||
}
|
||||
|
||||
/// Checks if the tree roots and inner data structures of `self` and `other` are equal.
|
||||
|
@ -463,7 +448,7 @@ impl From<Vec<jubjub::Fq>> for NoteCommitmentTree {
|
|||
/// A serialized Sapling note commitment tree.
|
||||
///
|
||||
/// The format of the serialized data is compatible with
|
||||
/// [`CommitmentTree`](merkle_tree::CommitmentTree) from `librustzcash` and not
|
||||
/// [`CommitmentTree`](incrementalmerkletree::frontier::CommitmentTree) from `librustzcash` and not
|
||||
/// with [`Frontier`](bridgetree::Frontier) from the crate
|
||||
/// [`incrementalmerkletree`]. Zebra follows the former format in order to stay
|
||||
/// consistent with `zcashd` in RPCs. Note that [`NoteCommitmentTree`] itself is
|
||||
|
@ -472,7 +457,7 @@ impl From<Vec<jubjub::Fq>> for NoteCommitmentTree {
|
|||
/// The formats are semantically equivalent. The primary difference between them
|
||||
/// is that in [`Frontier`](bridgetree::Frontier), the vector of parents is
|
||||
/// dense (we know where the gaps are from the position of the leaf in the
|
||||
/// overall tree); whereas in [`CommitmentTree`](merkle_tree::CommitmentTree),
|
||||
/// overall tree); whereas in [`CommitmentTree`](incrementalmerkletree::frontier::CommitmentTree),
|
||||
/// the vector of parent hashes is sparse with [`None`] values in the gaps.
|
||||
///
|
||||
/// The sparse format, used in this implementation, allows representing invalid
|
||||
|
@ -489,6 +474,9 @@ impl From<&NoteCommitmentTree> for SerializedTree {
|
|||
fn from(tree: &NoteCommitmentTree) -> Self {
|
||||
let mut serialized_tree = vec![];
|
||||
|
||||
//
|
||||
let legacy_tree = LegacyNoteCommitmentTree::from(tree.clone());
|
||||
|
||||
// Convert the note commitment tree represented as a frontier into the
|
||||
// format compatible with `zcashd`.
|
||||
//
|
||||
|
@ -502,20 +490,22 @@ impl From<&NoteCommitmentTree> for SerializedTree {
|
|||
// sparse formats for Sapling.
|
||||
//
|
||||
// [1]: <https://github.com/zcash/librustzcash/blob/a63a37a/zcash_primitives/src/merkle_tree.rs#L125>
|
||||
if let Some(frontier) = tree.inner.value() {
|
||||
let (left_leaf, right_leaf) = match frontier.leaf() {
|
||||
Leaf::Left(left_value) => (Some(left_value), None),
|
||||
Leaf::Right(left_value, right_value) => (Some(left_value), Some(right_value)),
|
||||
if let Some(frontier) = legacy_tree.inner.frontier {
|
||||
let (left_leaf, right_leaf) = match frontier.leaf {
|
||||
LegacyLeaf::Left(left_value) => (Some(left_value), None),
|
||||
LegacyLeaf::Right(left_value, right_value) => (Some(left_value), Some(right_value)),
|
||||
};
|
||||
|
||||
// Ommers are siblings of parent nodes along the branch from the
|
||||
// most recent leaf to the root of the tree.
|
||||
let mut ommers_iter = frontier.ommers().iter();
|
||||
let mut ommers_iter = frontier.ommers.iter();
|
||||
|
||||
// Set bits in the binary representation of the position indicate
|
||||
// the presence of ommers along the branch from the most recent leaf
|
||||
// node to the root of the tree, except for the lowest bit.
|
||||
let mut position: usize = frontier.position().into();
|
||||
let mut position: u64 = (frontier.position.0)
|
||||
.try_into()
|
||||
.expect("old usize position always fit in u64");
|
||||
|
||||
// The lowest bit does not indicate the presence of any ommers. We
|
||||
// clear it so that we can test if there are no set bits left in
|
||||
|
@ -552,7 +542,6 @@ impl From<&NoteCommitmentTree> for SerializedTree {
|
|||
}
|
||||
|
||||
// Serialize the converted note commitment tree.
|
||||
|
||||
Optional::write(&mut serialized_tree, left_leaf, |tree, leaf| {
|
||||
leaf.write(tree)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
//! Sapling serialization legacy code.
|
||||
//!
|
||||
//! We create a [`LegacyNoteCommitmentTree`] which is a copy of [`NoteCommitmentTree`] but where serialization and
|
||||
//! deserialization can be derived.
|
||||
//! To do this we create a [`LegacyFrontier`] which is a legacy `Frontier` structure that can be found in [1],
|
||||
//! In order to make [`LegacyFrontier`] serializable we also have our own versions of `NonEmptyFrontier` ([`LegacyNonEmptyFrontier`]),
|
||||
//! `Leaf`([`LegacyLeaf`]) and `Position`([`LegacyPosition`]) that can be found in [1] or [2].
|
||||
//!
|
||||
//! Conversions methods to/from [`LegacyNoteCommitmentTree`] to/from [`NoteCommitmentTree`] are defined also in this file.
|
||||
//!
|
||||
//! [1]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/bridgetree.rs
|
||||
//! [2]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/lib.rs
|
||||
|
||||
use incrementalmerkletree::{frontier::Frontier, Position};
|
||||
|
||||
use super::{Node, NoteCommitmentTree, Root, MERKLE_DEPTH};
|
||||
|
||||
/// A legacy version of [`NoteCommitmentTree`].
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename = "NoteCommitmentTree")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyNoteCommitmentTree {
|
||||
pub inner: LegacyFrontier<Node, MERKLE_DEPTH>,
|
||||
cached_root: std::sync::RwLock<Option<Root>>,
|
||||
}
|
||||
|
||||
impl From<NoteCommitmentTree> for LegacyNoteCommitmentTree {
|
||||
fn from(nct: NoteCommitmentTree) -> Self {
|
||||
LegacyNoteCommitmentTree {
|
||||
inner: nct.inner.into(),
|
||||
cached_root: nct.cached_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyNoteCommitmentTree> for NoteCommitmentTree {
|
||||
fn from(nct: LegacyNoteCommitmentTree) -> Self {
|
||||
NoteCommitmentTree {
|
||||
inner: nct.inner.into(),
|
||||
cached_root: nct.cached_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Frontier")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyFrontier<H, const DEPTH: u8> {
|
||||
pub frontier: Option<LegacyNonEmptyFrontier<H>>,
|
||||
}
|
||||
|
||||
impl From<LegacyFrontier<Node, MERKLE_DEPTH>> for Frontier<Node, MERKLE_DEPTH> {
|
||||
fn from(legacy_frontier: LegacyFrontier<Node, MERKLE_DEPTH>) -> Self {
|
||||
if let Some(legacy_frontier_data) = legacy_frontier.frontier {
|
||||
let mut ommers = legacy_frontier_data.ommers;
|
||||
let position = Position::from(
|
||||
u64::try_from(legacy_frontier_data.position.0)
|
||||
.expect("old `usize` always fits in `u64`"),
|
||||
);
|
||||
let leaf = match legacy_frontier_data.leaf {
|
||||
LegacyLeaf::Left(a) => a,
|
||||
LegacyLeaf::Right(a, b) => {
|
||||
ommers.insert(0, a);
|
||||
b
|
||||
}
|
||||
};
|
||||
Frontier::from_parts(
|
||||
position,
|
||||
leaf,
|
||||
ommers,
|
||||
)
|
||||
.expect("We should be able to construct a frontier from parts given legacy frontier is not empty")
|
||||
} else {
|
||||
Frontier::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frontier<Node, MERKLE_DEPTH>> for LegacyFrontier<Node, MERKLE_DEPTH> {
|
||||
fn from(frontier: Frontier<Node, MERKLE_DEPTH>) -> Self {
|
||||
if let Some(frontier_data) = frontier.value() {
|
||||
let leaf_from_frontier = *frontier_data.leaf();
|
||||
let mut leaf = LegacyLeaf::Left(leaf_from_frontier);
|
||||
let mut ommers = frontier_data.ommers().to_vec();
|
||||
let position = usize::try_from(u64::from(frontier_data.position()))
|
||||
.expect("new position should fit in a `usize`");
|
||||
if frontier_data.position().is_odd() {
|
||||
let left = ommers.remove(0);
|
||||
leaf = LegacyLeaf::Right(left, leaf_from_frontier);
|
||||
}
|
||||
LegacyFrontier {
|
||||
frontier: Some(LegacyNonEmptyFrontier {
|
||||
position: LegacyPosition(position),
|
||||
leaf,
|
||||
ommers: ommers.to_vec(),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
LegacyFrontier { frontier: None }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "NonEmptyFrontier")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyNonEmptyFrontier<H> {
|
||||
pub position: LegacyPosition,
|
||||
pub leaf: LegacyLeaf<H>,
|
||||
pub ommers: Vec<H>,
|
||||
}
|
||||
|
||||
/// A set of leaves of a Merkle tree.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Leaf")]
|
||||
#[allow(missing_docs)]
|
||||
pub enum LegacyLeaf<A> {
|
||||
Left(A),
|
||||
Right(A, A),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyPosition(pub usize);
|
|
@ -13,13 +13,16 @@
|
|||
use std::fmt;
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use incrementalmerkletree::{bridgetree, Frontier};
|
||||
use incrementalmerkletree::frontier::Frontier;
|
||||
use lazy_static::lazy_static;
|
||||
use sha2::digest::generic_array::GenericArray;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::commitment::NoteCommitment;
|
||||
|
||||
pub mod legacy;
|
||||
use legacy::LegacyNoteCommitmentTree;
|
||||
|
||||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
use proptest_derive::Arbitrary;
|
||||
|
||||
|
@ -128,7 +131,7 @@ impl From<&Root> for [u8; 32] {
|
|||
|
||||
/// A node of the Sprout note commitment tree.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
struct Node([u8; 32]);
|
||||
pub struct Node([u8; 32]);
|
||||
|
||||
impl fmt::Debug for Node {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -147,12 +150,12 @@ impl incrementalmerkletree::Hashable for Node {
|
|||
/// Note that Sprout does not use the `level` argument.
|
||||
///
|
||||
/// [MerkleCRH^Sprout]: https://zips.z.cash/protocol/protocol.pdf#sproutmerklecrh
|
||||
fn combine(_level: incrementalmerkletree::Altitude, a: &Self, b: &Self) -> Self {
|
||||
fn combine(_level: incrementalmerkletree::Level, a: &Self, b: &Self) -> Self {
|
||||
Self(merkle_crh_sprout(a.0, b.0))
|
||||
}
|
||||
|
||||
/// Returns the node for the level below the given level. (A quirk of the API)
|
||||
fn empty_root(level: incrementalmerkletree::Altitude) -> Self {
|
||||
fn empty_root(level: incrementalmerkletree::Level) -> Self {
|
||||
let layer_below = usize::from(MERKLE_DEPTH) - usize::from(level);
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
|
@ -200,16 +203,18 @@ pub enum NoteCommitmentTreeError {
|
|||
/// job of this tree to protect against double-spending, as it is append-only; double-spending
|
||||
/// is prevented by maintaining the [nullifier set] for each shielded pool.
|
||||
///
|
||||
/// Internally this wraps [`incrementalmerkletree::bridgetree::Frontier`], so that we can maintain and increment
|
||||
/// Internally this wraps [`bridgetree::Frontier`], so that we can maintain and increment
|
||||
/// the full tree with only the minimal amount of non-empty nodes/leaves required.
|
||||
///
|
||||
/// [Sprout Note Commitment Tree]: https://zips.z.cash/protocol/protocol.pdf#merkletree
|
||||
/// [nullifier set]: https://zips.z.cash/protocol/protocol.pdf#nullifierset
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(into = "LegacyNoteCommitmentTree")]
|
||||
#[serde(from = "LegacyNoteCommitmentTree")]
|
||||
pub struct NoteCommitmentTree {
|
||||
/// The tree represented as a [`incrementalmerkletree::bridgetree::Frontier`].
|
||||
/// The tree represented as a [`bridgetree::Frontier`].
|
||||
///
|
||||
/// A [`incrementalmerkletree::Frontier`] is a subset of the tree that allows to fully specify it. It
|
||||
/// A [`bridgetree::Frontier`] is a subset of the tree that allows to fully specify it. It
|
||||
/// consists of nodes along the rightmost (newer) branch of the tree that
|
||||
/// has non-empty nodes. Upper (near root) empty nodes of the branch are not
|
||||
/// stored.
|
||||
|
@ -222,7 +227,7 @@ pub struct NoteCommitmentTree {
|
|||
/// <https://zips.z.cash/protocol/protocol.pdf#merkletree>
|
||||
///
|
||||
/// Note: MerkleDepth^Sprout = MERKLE_DEPTH = 29.
|
||||
inner: bridgetree::Frontier<Node, MERKLE_DEPTH>,
|
||||
inner: Frontier<Node, MERKLE_DEPTH>,
|
||||
|
||||
/// A cached root of the tree.
|
||||
///
|
||||
|
@ -248,7 +253,7 @@ impl NoteCommitmentTree {
|
|||
/// Returns an error if the tree is full.
|
||||
#[allow(clippy::unwrap_in_result)]
|
||||
pub fn append(&mut self, cm: NoteCommitment) -> Result<(), NoteCommitmentTreeError> {
|
||||
if self.inner.append(&cm.into()) {
|
||||
if self.inner.append(cm.into()) {
|
||||
// Invalidate cached root
|
||||
let cached_root = self
|
||||
.cached_root
|
||||
|
@ -323,7 +328,9 @@ impl NoteCommitmentTree {
|
|||
///
|
||||
/// [spec]: https://zips.z.cash/protocol/protocol.pdf#merkletree
|
||||
pub fn count(&self) -> u64 {
|
||||
self.inner.position().map_or(0, |pos| u64::from(pos) + 1)
|
||||
self.inner
|
||||
.value()
|
||||
.map_or(0, |x| u64::from(x.position()) + 1)
|
||||
}
|
||||
|
||||
/// Checks if the tree roots and inner data structures of `self` and `other` are equal.
|
||||
|
@ -360,7 +367,7 @@ impl Clone for NoteCommitmentTree {
|
|||
impl Default for NoteCommitmentTree {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: bridgetree::Frontier::empty(),
|
||||
inner: Frontier::empty(),
|
||||
cached_root: Default::default(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
//! Sprout serialization legacy code.
|
||||
//!
|
||||
//! We create a [`LegacyNoteCommitmentTree`] which is a copy of [`NoteCommitmentTree`] but where serialization and
|
||||
//! deserialization can be derived.
|
||||
//! To do this we create a [`LegacyFrontier`] which is a legacy `Frontier` structure that can be found in [1],
|
||||
//! In order to make [`LegacyFrontier`] serializable we also have our own versions of `NonEmptyFrontier` ([`LegacyNonEmptyFrontier`]),
|
||||
//! `Leaf`([`LegacyLeaf`]) and `Position`([`LegacyPosition`]) that can be found in [1] or [2].
|
||||
//!
|
||||
//! Conversions methods to/from [`LegacyNoteCommitmentTree`] to/from [`NoteCommitmentTree`] are defined also in this file.
|
||||
//!
|
||||
//! [1]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/bridgetree.rs
|
||||
//! [2]: https://github.com/zcash/incrementalmerkletree/blob/incrementalmerkletree-v0.3.1/src/lib.rs
|
||||
|
||||
use incrementalmerkletree::{frontier::Frontier, Position};
|
||||
|
||||
use super::{Node, NoteCommitmentTree, Root, MERKLE_DEPTH};
|
||||
|
||||
/// A legacy version of [`NoteCommitmentTree`].
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename = "NoteCommitmentTree")]
|
||||
pub struct LegacyNoteCommitmentTree {
|
||||
inner: LegacyFrontier<Node, MERKLE_DEPTH>,
|
||||
cached_root: std::sync::RwLock<Option<Root>>,
|
||||
}
|
||||
|
||||
impl From<NoteCommitmentTree> for LegacyNoteCommitmentTree {
|
||||
fn from(nct: NoteCommitmentTree) -> Self {
|
||||
LegacyNoteCommitmentTree {
|
||||
inner: nct.inner.into(),
|
||||
cached_root: nct.cached_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyNoteCommitmentTree> for NoteCommitmentTree {
|
||||
fn from(nct: LegacyNoteCommitmentTree) -> Self {
|
||||
NoteCommitmentTree {
|
||||
inner: nct.inner.into(),
|
||||
cached_root: nct.cached_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Frontier")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct LegacyFrontier<H, const DEPTH: u8> {
|
||||
frontier: Option<LegacyNonEmptyFrontier<H>>,
|
||||
}
|
||||
|
||||
impl From<LegacyFrontier<Node, MERKLE_DEPTH>> for Frontier<Node, MERKLE_DEPTH> {
|
||||
fn from(legacy_frontier: LegacyFrontier<Node, MERKLE_DEPTH>) -> Self {
|
||||
if let Some(legacy_frontier_data) = legacy_frontier.frontier {
|
||||
let mut ommers = legacy_frontier_data.ommers;
|
||||
let position = Position::from(
|
||||
u64::try_from(legacy_frontier_data.position.0)
|
||||
.expect("old `usize` always fits in `u64`"),
|
||||
);
|
||||
let leaf = match legacy_frontier_data.leaf {
|
||||
LegacyLeaf::Left(a) => a,
|
||||
LegacyLeaf::Right(a, b) => {
|
||||
ommers.insert(0, a);
|
||||
b
|
||||
}
|
||||
};
|
||||
Frontier::from_parts(
|
||||
position,
|
||||
leaf,
|
||||
ommers,
|
||||
)
|
||||
.expect("We should be able to construct a frontier from parts given legacy frontier is not empty")
|
||||
} else {
|
||||
Frontier::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frontier<Node, MERKLE_DEPTH>> for LegacyFrontier<Node, MERKLE_DEPTH> {
|
||||
fn from(frontier: Frontier<Node, MERKLE_DEPTH>) -> Self {
|
||||
if let Some(frontier_data) = frontier.value() {
|
||||
let leaf_from_frontier = *frontier_data.leaf();
|
||||
let mut leaf = LegacyLeaf::Left(leaf_from_frontier);
|
||||
let mut ommers = frontier_data.ommers().to_vec();
|
||||
let position = usize::try_from(u64::from(frontier_data.position()))
|
||||
.expect("new position should fit in a `usize`");
|
||||
if frontier_data.position().is_odd() {
|
||||
let left = ommers.remove(0);
|
||||
leaf = LegacyLeaf::Right(left, leaf_from_frontier);
|
||||
}
|
||||
LegacyFrontier {
|
||||
frontier: Some(LegacyNonEmptyFrontier {
|
||||
position: LegacyPosition(position),
|
||||
leaf,
|
||||
ommers: ommers.to_vec(),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
LegacyFrontier { frontier: None }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "NonEmptyFrontier")]
|
||||
struct LegacyNonEmptyFrontier<H> {
|
||||
position: LegacyPosition,
|
||||
leaf: LegacyLeaf<H>,
|
||||
ommers: Vec<H>,
|
||||
}
|
||||
|
||||
/// A set of leaves of a Merkle tree.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename = "Leaf")]
|
||||
enum LegacyLeaf<A> {
|
||||
Left(A),
|
||||
Right(A, A),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
struct LegacyPosition(usize);
|
|
@ -58,9 +58,9 @@ tower = { version = "0.4.13", features = ["timeout", "util", "buffer"] }
|
|||
tracing = "0.1.37"
|
||||
tracing-futures = "0.2.5"
|
||||
|
||||
orchard = "0.4.0"
|
||||
orchard = "0.5.0"
|
||||
|
||||
zcash_proofs = { version = "0.11.0", features = ["local-prover", "multicore", "download-params"] }
|
||||
zcash_proofs = { version = "0.12.1", features = ["local-prover", "multicore", "download-params"] }
|
||||
|
||||
tower-fallback = { path = "../tower-fallback/", version = "0.2.41-beta.3" }
|
||||
tower-batch-control = { path = "../tower-batch-control/", version = "0.2.41-beta.3" }
|
||||
|
|
|
@ -65,7 +65,7 @@ serde = { version = "1.0.168", features = ["serde_derive"] }
|
|||
# Experimental feature getblocktemplate-rpcs
|
||||
rand = { version = "0.8.5", optional = true }
|
||||
# ECC deps used by getblocktemplate-rpcs feature
|
||||
zcash_address = { version = "0.2.1", optional = true }
|
||||
zcash_address = { version = "0.3.0", optional = true }
|
||||
|
||||
# Test-only feature proptest-impl
|
||||
proptest = { version = "1.2.0", optional = true }
|
||||
|
|
|
@ -15,7 +15,7 @@ keywords = ["zebra", "zcash"]
|
|||
categories = ["api-bindings", "cryptography::cryptocurrencies"]
|
||||
|
||||
[dependencies]
|
||||
zcash_script = "0.1.12"
|
||||
zcash_script = "0.1.13"
|
||||
|
||||
zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.27" }
|
||||
|
||||
|
|
|
@ -10,9 +10,16 @@ use rand::random;
|
|||
use halo2::pasta::{group::ff::PrimeField, pallas};
|
||||
|
||||
use zebra_chain::{
|
||||
orchard::tree::NoteCommitmentTree as OrchardNoteCommitmentTree,
|
||||
sapling::tree::NoteCommitmentTree as SaplingNoteCommitmentTree,
|
||||
orchard::{
|
||||
tree::legacy::LegacyNoteCommitmentTree as LegacyOrchardNoteCommitmentTree,
|
||||
tree::NoteCommitmentTree as OrchardNoteCommitmentTree,
|
||||
},
|
||||
sapling::{
|
||||
tree::legacy::LegacyNoteCommitmentTree as LegacySaplingNoteCommitmentTree,
|
||||
tree::NoteCommitmentTree as SaplingNoteCommitmentTree,
|
||||
},
|
||||
sprout::{
|
||||
tree::legacy::LegacyNoteCommitmentTree as LegacySproutNoteCommitmentTree,
|
||||
tree::NoteCommitmentTree as SproutNoteCommitmentTree,
|
||||
NoteCommitment as SproutNoteCommitment,
|
||||
},
|
||||
|
@ -20,26 +27,6 @@ use zebra_chain::{
|
|||
|
||||
use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk};
|
||||
|
||||
// Currently, these tests check these structs are equal:
|
||||
// * commitments -> tree struct
|
||||
// * commitments -> tree struct -> serialize -> deserialize -> tree struct
|
||||
// And these serialized formats are equal:
|
||||
// * fixed serialized test vector
|
||||
// * commitments -> tree struct -> serialize
|
||||
// * commitments -> tree struct -> serialize -> deserialize -> tree struct -> serialize
|
||||
//
|
||||
// TODO: apply these tests to the new tree structs, and update the serialization format
|
||||
// (keeping the tests for the old format is optional, because the tests below cover it)
|
||||
//
|
||||
// TODO: test that old and new serializations produce the same format:
|
||||
// Tree roots built from the same commitments should match:
|
||||
// * commitments -> old tree struct -> new tree struct -> un-cached root
|
||||
// * commitments -> new tree struct -> un-cached root
|
||||
// Even when serialized and deserialized:
|
||||
// * commitments -> old tree struct -> old serialize -> old deserialize -> old tree struct -> new tree struct -> un-cached root
|
||||
// * commitments -> new tree struct -> new serialize -> new deserialize -> new tree struct -> un-cached root
|
||||
// * commitments -> new tree struct -> un-cached root
|
||||
|
||||
/// Check that the sprout tree database serialization format has not changed.
|
||||
#[test]
|
||||
fn sprout_note_commitment_tree_serialization() {
|
||||
|
@ -73,21 +60,8 @@ fn sprout_note_commitment_tree_serialization() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "010200836045484077cf6390184ea7cd48b460e2d0f22b2293b69633bb152314a692fb019f5b2b1e4bf7e7318d0a1f417ca6bca36077025b3d11e074b94cd55ce9f3861801c45297124f50dcd3f78eed017afd1e30764cd74cdf0a57751978270fd0721359";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SproutNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
sprout_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the sprout tree database serialization format has not changed for one commitment.
|
||||
|
@ -119,21 +93,8 @@ fn sprout_note_commitment_tree_serialization_one() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "010000836045484077cf6390184ea7cd48b460e2d0f22b2293b69633bb152314a692fb000193e5f97ce1d5d94d0c6e1b66a4a262c9ae89e56e28f3f6e4a557b6fb70e173a8";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SproutNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
sprout_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the sprout tree database serialization format has not changed when the number of
|
||||
|
@ -174,21 +135,8 @@ fn sprout_note_commitment_tree_serialization_pow2() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "010301836045484077cf6390184ea7cd48b460e2d0f22b2293b69633bb152314a692fb92498a8295ea36d593eaee7cb8b55be3a3e37b8185d3807693184054cd574ae4019f5b2b1e4bf7e7318d0a1f417ca6bca36077025b3d11e074b94cd55ce9f3861801b61f588fcba9cea79e94376adae1c49583f716d2f20367141f1369a235b95c98";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SproutNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
sprout_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the sapling tree database serialization format has not changed.
|
||||
|
@ -224,21 +172,8 @@ fn sapling_note_commitment_tree_serialization() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "0102007c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e036c0162324ff2c329e99193a74d28a585a3c167a93bf41a255135529c913bd9b1e66601ddaa1ab86de5c153993414f34ba97e9674c459dfadde112b89eeeafa0e5a204c";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SaplingNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
sapling_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the sapling tree database serialization format has not changed for one commitment.
|
||||
|
@ -270,21 +205,8 @@ fn sapling_note_commitment_tree_serialization_one() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "010000225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b1145800012c60c7de033d7539d123fb275011edfe08d57431676981d162c816372063bc71";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SaplingNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
sapling_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the sapling tree database serialization format has not changed when the number of
|
||||
|
@ -329,21 +251,8 @@ fn sapling_note_commitment_tree_serialization_pow2() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "010701f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c3a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac15025991131c5c25911b35fcea2a8343e2dfd7a4d5b45493390e0cb184394d91c349002df68503da9247dfde6585cb8c9fa94897cf21735f8fc1b32116ef474de05c01d23765f3d90dfd97817ed6d995bd253d85967f77b9f1eaef6ecbcb0ef6796812";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SaplingNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
sapling_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the orchard tree database serialization format has not changed.
|
||||
|
@ -389,21 +298,8 @@ fn orchard_note_commitment_tree_serialization() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "010200ee9488053a30c596b43014105d3477e6f578c89240d1d1ee1743b77bb6adc40a01a34b69a4e4d9ccf954d46e5da1004d361a5497f511aeb4d481d23c0be177813301a0be6dab19bc2c65d8299258c16e14d48ec4d4959568c6412aa85763c222a702";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = OrchardNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
orchard_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the orchard tree database serialization format has not changed for one commitment.
|
||||
|
@ -437,21 +333,8 @@ fn orchard_note_commitment_tree_serialization_one() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "01000068135cf49933229099a44ec99a75e1e1cb4640f9b5bdec6b3223856fea16390a000178afd4da59c541e9c2f317f9aff654f1fb38d14dc99431cbbfa93601c7068117";
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = OrchardNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
orchard_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
/// Check that the orchard tree database serialization format has not changed when the number of
|
||||
|
@ -496,19 +379,156 @@ fn orchard_note_commitment_tree_serialization_pow2() {
|
|||
// The purpose of this test is to make sure the serialization format does
|
||||
// not change by accident.
|
||||
let expected_serialized_tree_hex = "01010178315008fb2998b430a5731d6726207dc0f0ec81ea64af5cf612956901e72f0eee9488053a30c596b43014105d3477e6f578c89240d1d1ee1743b77bb6adc40a0001d3d525931005e45f5a29bc82524e871e5ee1b6d77839deb741a6e50cd99fdf1a";
|
||||
|
||||
orchard_checks(incremental_tree, expected_serialized_tree_hex);
|
||||
}
|
||||
|
||||
fn sprout_checks(incremental_tree: SproutNoteCommitmentTree, expected_serialized_tree_hex: &str) {
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SproutNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// Get a legacy deserialized tree from the deserialized tree.
|
||||
let deserialized_legacy_tree = LegacySproutNoteCommitmentTree::from(deserialized_tree.clone());
|
||||
|
||||
// Get a deserialized tree from a legacy deserialized tree.
|
||||
let deserialized_legacy_tree_as_new = deserialized_legacy_tree.into();
|
||||
|
||||
// Check frontiers are the same.
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
incremental_tree.assert_frontier_eq(&deserialized_legacy_tree_as_new);
|
||||
|
||||
// Check cached roots are the same.
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
assert_eq!(
|
||||
incremental_tree.root(),
|
||||
deserialized_legacy_tree_as_new.root()
|
||||
);
|
||||
|
||||
// Check recalculated roots are the same
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_tree.recalculate_root()
|
||||
);
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_legacy_tree_as_new.recalculate_root()
|
||||
);
|
||||
|
||||
// Check reclaculated and cached roots are the same
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_tree
|
||||
.cached_root()
|
||||
.expect("cached root was serialized")
|
||||
);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
let re_serialized_legacy_tree = deserialized_legacy_tree_as_new.as_bytes();
|
||||
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
assert_eq!(re_serialized_legacy_tree, re_serialized_tree);
|
||||
}
|
||||
|
||||
fn sapling_checks(incremental_tree: SaplingNoteCommitmentTree, expected_serialized_tree_hex: &str) {
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = SaplingNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// Get a legacy deserialized tree from the deserialized tree.
|
||||
let deserialized_legacy_tree = LegacySaplingNoteCommitmentTree::from(deserialized_tree.clone());
|
||||
|
||||
// Get a deserialized tree from a legacy deserialized tree.
|
||||
let deserialized_legacy_tree_as_new = deserialized_legacy_tree.into();
|
||||
|
||||
// Check frontiers are the same.
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
incremental_tree.assert_frontier_eq(&deserialized_legacy_tree_as_new);
|
||||
|
||||
// Check cached roots are the same.
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
assert_eq!(
|
||||
incremental_tree.root(),
|
||||
deserialized_legacy_tree_as_new.root()
|
||||
);
|
||||
|
||||
// Check recalculated roots are the same
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_tree.recalculate_root()
|
||||
);
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_legacy_tree_as_new.recalculate_root()
|
||||
);
|
||||
|
||||
// Check reclaculated and cached roots are the same
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_tree
|
||||
.cached_root()
|
||||
.expect("cached root was serialized")
|
||||
);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
let re_serialized_legacy_tree = deserialized_legacy_tree_as_new.as_bytes();
|
||||
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
assert_eq!(re_serialized_legacy_tree, re_serialized_tree);
|
||||
}
|
||||
|
||||
fn orchard_checks(incremental_tree: OrchardNoteCommitmentTree, expected_serialized_tree_hex: &str) {
|
||||
let serialized_tree = incremental_tree.as_bytes();
|
||||
|
||||
assert_eq!(hex::encode(&serialized_tree), expected_serialized_tree_hex);
|
||||
|
||||
let deserialized_tree = OrchardNoteCommitmentTree::from_bytes(&serialized_tree);
|
||||
|
||||
// This check isn't enough to show that the entire struct is the same, because it just compares
|
||||
// the cached serialized/deserialized roots. (NoteCommitmentTree::eq() also just compares
|
||||
// roots.)
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
// Get a legacy deserialized tree from the deserialized tree.
|
||||
let deserialized_legacy_tree = LegacyOrchardNoteCommitmentTree::from(deserialized_tree.clone());
|
||||
|
||||
// Get a deserialized tree from a legacy deserialized tree.
|
||||
let deserialized_legacy_tree_as_new = deserialized_legacy_tree.into();
|
||||
|
||||
// Check frontiers are the same.
|
||||
incremental_tree.assert_frontier_eq(&deserialized_tree);
|
||||
incremental_tree.assert_frontier_eq(&deserialized_legacy_tree_as_new);
|
||||
|
||||
// Check cached roots are the same.
|
||||
assert_eq!(incremental_tree.root(), deserialized_tree.root());
|
||||
assert_eq!(
|
||||
incremental_tree.root(),
|
||||
deserialized_legacy_tree_as_new.root()
|
||||
);
|
||||
|
||||
// Check recalculated roots are the same
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_tree.recalculate_root()
|
||||
);
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_legacy_tree_as_new.recalculate_root()
|
||||
);
|
||||
|
||||
// Check reclaculated and cached roots are the same
|
||||
assert_eq!(
|
||||
incremental_tree.recalculate_root(),
|
||||
deserialized_tree
|
||||
.cached_root()
|
||||
.expect("cached root was serialized")
|
||||
);
|
||||
|
||||
// Double-check that the internal format is the same by re-serializing the tree.
|
||||
let re_serialized_tree = deserialized_tree.as_bytes();
|
||||
let re_serialized_legacy_tree = deserialized_legacy_tree_as_new.as_bytes();
|
||||
|
||||
assert_eq!(serialized_tree, re_serialized_tree);
|
||||
assert_eq!(re_serialized_legacy_tree, re_serialized_tree);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue