Calculate incremental note commitment trees (#2407)
* Make sapling::tree::NoteCommitmentTree incrementally updateable * Make orchard::tree::NoteCommitmentTree incrementally updateable * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * Changed to incrementalmerkletree implementation; update MerkleCRH^Orchard * Improvements from review; organize file names (vectors.rs -> tree.rs) Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: teor <teor@riseup.net> Co-authored-by: Conrado P. L. Gouvea <conradoplg@gmail.com>
This commit is contained in:
parent
12c60b5868
commit
48cf52735c
|
@ -1726,6 +1726,15 @@ dependencies = [
|
|||
"version_check 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "incrementalmerkletree"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16d75fb342984cc8cea665a9ef5607b4956569839ca098172082fa1b19bdaf02"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.0"
|
||||
|
@ -4506,6 +4515,7 @@ dependencies = [
|
|||
"group",
|
||||
"halo2",
|
||||
"hex",
|
||||
"incrementalmerkletree",
|
||||
"itertools 0.10.1",
|
||||
"jubjub",
|
||||
"lazy_static",
|
||||
|
|
|
@ -46,6 +46,7 @@ zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "0
|
|||
bigint = "4"
|
||||
uint = "0.9.1"
|
||||
bls12_381 = "0.5.0"
|
||||
incrementalmerkletree = "0.1.0"
|
||||
|
||||
proptest = { version = "0.10", optional = true }
|
||||
proptest-derive = { version = "0.3.0", optional = true }
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mod preallocate;
|
||||
mod prop;
|
||||
pub mod test_vectors;
|
||||
mod test_vectors;
|
||||
mod tree;
|
||||
|
|
|
@ -167,3 +167,355 @@ pub const EMPTY_ROOTS: [[u8; 32]; 33] = [
|
|||
0xd8, 0x2f,
|
||||
],
|
||||
];
|
||||
|
||||
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_merkle_tree.py
|
||||
pub const COMMITMENTS: [[[u8; 32]; 5]; 10] = [
|
||||
[
|
||||
[
|
||||
0x68, 0x13, 0x5c, 0xf4, 0x99, 0x33, 0x22, 0x90, 0x99, 0xa4, 0x4e, 0xc9, 0x9a, 0x75,
|
||||
0xe1, 0xe1, 0xcb, 0x46, 0x40, 0xf9, 0xb5, 0xbd, 0xec, 0x6b, 0x32, 0x23, 0x85, 0x6f,
|
||||
0xea, 0x16, 0x39, 0x0a,
|
||||
],
|
||||
[
|
||||
0x78, 0x31, 0x50, 0x08, 0xfb, 0x29, 0x98, 0xb4, 0x30, 0xa5, 0x73, 0x1d, 0x67, 0x26,
|
||||
0x20, 0x7d, 0xc0, 0xf0, 0xec, 0x81, 0xea, 0x64, 0xaf, 0x5c, 0xf6, 0x12, 0x95, 0x69,
|
||||
0x01, 0xe7, 0x2f, 0x0e,
|
||||
],
|
||||
[
|
||||
0xee, 0x94, 0x88, 0x05, 0x3a, 0x30, 0xc5, 0x96, 0xb4, 0x30, 0x14, 0x10, 0x5d, 0x34,
|
||||
0x77, 0xe6, 0xf5, 0x78, 0xc8, 0x92, 0x40, 0xd1, 0xd1, 0xee, 0x17, 0x43, 0xb7, 0x7b,
|
||||
0xb6, 0xad, 0xc4, 0x0a,
|
||||
],
|
||||
[
|
||||
0x9d, 0xdc, 0xe7, 0xf0, 0x65, 0x01, 0xf3, 0x63, 0x76, 0x8c, 0x5b, 0xca, 0x3f, 0x26,
|
||||
0x46, 0x60, 0x83, 0x4d, 0x4d, 0xf4, 0x46, 0xd1, 0x3e, 0xfc, 0xd7, 0xc6, 0xf1, 0x7b,
|
||||
0x16, 0x7a, 0xac, 0x1a,
|
||||
],
|
||||
[
|
||||
0xbd, 0x86, 0x16, 0x81, 0x1c, 0x6f, 0x5f, 0x76, 0x9e, 0xa4, 0x53, 0x9b, 0xba, 0xff,
|
||||
0x0f, 0x19, 0x8a, 0x6c, 0xdf, 0x3b, 0x28, 0x0d, 0xd4, 0x99, 0x26, 0x16, 0x3b, 0xd5,
|
||||
0x3f, 0x53, 0xa1, 0x21,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0x31, 0x11, 0xbe, 0x48, 0x8a, 0xfc, 0x50, 0x4c, 0x11, 0xaf, 0x2d, 0xfb, 0x35, 0xda,
|
||||
0x93, 0x2f, 0x65, 0x72, 0xee, 0xcd, 0x19, 0xa6, 0xc8, 0x54, 0x09, 0x5b, 0x01, 0x68,
|
||||
0x30, 0x88, 0xe8, 0x25,
|
||||
],
|
||||
[
|
||||
0x6f, 0x01, 0x84, 0x37, 0xde, 0x72, 0x2d, 0xa9, 0xa9, 0x88, 0x1c, 0x6d, 0x17, 0x6f,
|
||||
0xf4, 0x19, 0x60, 0x84, 0x4e, 0x6e, 0x0a, 0x3a, 0xd6, 0xcf, 0x8e, 0xdb, 0x6a, 0xf4,
|
||||
0xf1, 0xc2, 0x8a, 0x34,
|
||||
],
|
||||
[
|
||||
0x44, 0xc3, 0xb9, 0x59, 0xf9, 0xe5, 0x08, 0x0d, 0xfd, 0x55, 0x0a, 0x84, 0x82, 0x58,
|
||||
0x34, 0xfb, 0x39, 0x2b, 0x6e, 0xe6, 0x61, 0xf8, 0x9b, 0x2b, 0xfb, 0xa5, 0xfb, 0x45,
|
||||
0x44, 0x4e, 0x67, 0x3a,
|
||||
],
|
||||
[
|
||||
0x5e, 0xc3, 0x6d, 0xce, 0xb0, 0xa0, 0xb1, 0xf8, 0x1e, 0x3f, 0x16, 0x72, 0x49, 0x91,
|
||||
0x78, 0x1f, 0xae, 0xfa, 0x68, 0x38, 0x24, 0xe7, 0x4e, 0x81, 0x20, 0xf7, 0x46, 0x53,
|
||||
0x08, 0xe9, 0xd9, 0x21,
|
||||
],
|
||||
[
|
||||
0x9c, 0x35, 0xf2, 0x03, 0x20, 0x36, 0x09, 0x92, 0xf6, 0xa8, 0x8a, 0xa5, 0xc8, 0x70,
|
||||
0xcd, 0xb5, 0x9c, 0x45, 0x97, 0xfe, 0x08, 0xfb, 0xf8, 0xe1, 0xf3, 0xb2, 0x14, 0x73,
|
||||
0x3d, 0x89, 0x23, 0x2a,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0xca, 0x38, 0x0a, 0x17, 0xb6, 0xa0, 0xf2, 0x4e, 0xf0, 0x6d, 0x13, 0x9b, 0xaa, 0x1b,
|
||||
0x70, 0xe6, 0x78, 0xa7, 0x3d, 0x0e, 0x65, 0x78, 0x58, 0x04, 0xd8, 0x8c, 0x96, 0x6a,
|
||||
0xcf, 0x10, 0x37, 0x38,
|
||||
],
|
||||
[
|
||||
0xfe, 0xca, 0x8d, 0xf1, 0xc0, 0x4c, 0xda, 0x0c, 0xfc, 0xdc, 0x23, 0x4c, 0x17, 0x14,
|
||||
0x71, 0xdf, 0x7a, 0xad, 0x90, 0xac, 0x9e, 0x28, 0x64, 0xb6, 0xe1, 0xbe, 0x0d, 0xfc,
|
||||
0x6a, 0x40, 0x41, 0x23,
|
||||
],
|
||||
[
|
||||
0x83, 0x7d, 0x8d, 0xc9, 0xa8, 0x99, 0x1c, 0x17, 0x8d, 0x65, 0x02, 0x85, 0x0c, 0x1f,
|
||||
0x91, 0xb1, 0xd4, 0x1a, 0x97, 0x3e, 0xf5, 0xa9, 0xec, 0x4b, 0x10, 0xbf, 0x46, 0x24,
|
||||
0x67, 0xa7, 0xd4, 0x21,
|
||||
],
|
||||
[
|
||||
0xb6, 0x7b, 0xf5, 0x11, 0x92, 0xe3, 0x0e, 0x8b, 0x39, 0x9c, 0xed, 0xef, 0xd8, 0xb6,
|
||||
0x4f, 0x23, 0x86, 0xad, 0x1c, 0xac, 0x70, 0xbd, 0xcc, 0xd7, 0xca, 0xc1, 0x95, 0x1a,
|
||||
0x3e, 0x86, 0xb5, 0x37,
|
||||
],
|
||||
[
|
||||
0xce, 0x62, 0x6d, 0x31, 0x28, 0xca, 0x6c, 0x8f, 0xd2, 0x72, 0x21, 0x59, 0x12, 0xde,
|
||||
0x64, 0xfb, 0x74, 0xc4, 0x5d, 0x56, 0xf4, 0x37, 0x4c, 0xe5, 0xbf, 0x05, 0x37, 0xee,
|
||||
0x2e, 0x38, 0x20, 0x07,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0xfd, 0x22, 0x61, 0xb6, 0x2d, 0x4a, 0x70, 0x96, 0x40, 0x05, 0x44, 0x72, 0x03, 0x34,
|
||||
0x90, 0xb8, 0x3d, 0x18, 0x42, 0x02, 0xe0, 0x26, 0x72, 0xc2, 0xff, 0x12, 0x76, 0x6c,
|
||||
0x4e, 0x7d, 0x4f, 0x0b,
|
||||
],
|
||||
[
|
||||
0xef, 0xc2, 0xc8, 0xe1, 0x29, 0xb5, 0x1d, 0xc6, 0x2b, 0xcb, 0xdc, 0x3f, 0x3e, 0x1a,
|
||||
0x58, 0x2c, 0xd2, 0x83, 0xc7, 0x98, 0x07, 0x57, 0x53, 0xcd, 0x83, 0x12, 0x17, 0x33,
|
||||
0x81, 0xfa, 0x1f, 0x34,
|
||||
],
|
||||
[
|
||||
0x22, 0x3b, 0xc8, 0xe6, 0x9d, 0x13, 0x2b, 0xdd, 0x85, 0x07, 0x0c, 0xaf, 0x9e, 0x12,
|
||||
0xa1, 0xbb, 0xe2, 0xe1, 0x4a, 0x63, 0x0b, 0xec, 0x08, 0x44, 0x18, 0x1c, 0x01, 0x4e,
|
||||
0x57, 0x77, 0xd1, 0x15,
|
||||
],
|
||||
[
|
||||
0xc2, 0x0a, 0x21, 0xa5, 0xe2, 0x0a, 0x76, 0xd6, 0xc5, 0x05, 0xe9, 0x67, 0xcb, 0x9d,
|
||||
0x50, 0xd7, 0x53, 0xe3, 0xd1, 0x35, 0x3c, 0xce, 0x32, 0x6e, 0xef, 0x55, 0x5f, 0xa3,
|
||||
0x31, 0xb6, 0x9c, 0x21,
|
||||
],
|
||||
[
|
||||
0xd0, 0xc7, 0x3f, 0x0f, 0x98, 0x76, 0x3b, 0xed, 0x8b, 0x14, 0x80, 0x0f, 0x37, 0x88,
|
||||
0xc9, 0x80, 0x83, 0xc8, 0x0e, 0x1c, 0x88, 0x90, 0x46, 0x64, 0x4c, 0x95, 0x6f, 0x01,
|
||||
0xe2, 0x4b, 0x1d, 0x38,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0xcf, 0x79, 0xd1, 0xb1, 0x1e, 0x3b, 0xa2, 0x8f, 0xc2, 0x14, 0x8f, 0x21, 0xa6, 0x6f,
|
||||
0xe1, 0x89, 0xe0, 0xff, 0xb6, 0x78, 0x60, 0xd4, 0x91, 0x26, 0x5c, 0x18, 0x5a, 0x35,
|
||||
0xb3, 0x9b, 0xb5, 0x2b,
|
||||
],
|
||||
[
|
||||
0x69, 0x00, 0xdb, 0x33, 0x01, 0xa4, 0xfd, 0x80, 0x50, 0x2f, 0xa7, 0xf0, 0xcc, 0x83,
|
||||
0x42, 0x6e, 0x7a, 0xf2, 0x23, 0x1f, 0xcf, 0x61, 0x2c, 0x0d, 0x25, 0xb6, 0x84, 0x88,
|
||||
0x81, 0x3f, 0xcb, 0x11,
|
||||
],
|
||||
[
|
||||
0xfb, 0x46, 0x7e, 0xfc, 0xa6, 0xe1, 0xd5, 0x48, 0xc9, 0x30, 0x48, 0x75, 0xfc, 0x33,
|
||||
0xd3, 0xe5, 0x64, 0xf2, 0x9e, 0xe0, 0x99, 0x7e, 0xbd, 0x18, 0x7d, 0x98, 0x59, 0x10,
|
||||
0x03, 0x4d, 0xbc, 0x04,
|
||||
],
|
||||
[
|
||||
0x39, 0x0e, 0xaf, 0x40, 0x8e, 0x20, 0x9e, 0xf0, 0x69, 0xe1, 0xb3, 0xe6, 0xbc, 0x63,
|
||||
0xba, 0xce, 0x32, 0xd2, 0xf0, 0x68, 0x39, 0xd1, 0xfe, 0xd8, 0x18, 0x17, 0xbf, 0xcc,
|
||||
0x1f, 0x6f, 0xa6, 0x14,
|
||||
],
|
||||
[
|
||||
0xee, 0x60, 0xdf, 0x35, 0x19, 0xe0, 0xd9, 0xb0, 0x77, 0xf3, 0xcc, 0x29, 0x7c, 0x62,
|
||||
0x0e, 0xdd, 0xd5, 0x2e, 0x1e, 0x22, 0xb3, 0x7c, 0xe5, 0x5e, 0xc9, 0xf8, 0xe3, 0xc3,
|
||||
0x37, 0x0f, 0xb3, 0x11,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0x19, 0xa9, 0xe5, 0x13, 0x82, 0x6e, 0x11, 0x06, 0xa3, 0xa9, 0x5f, 0x5e, 0x7a, 0x71,
|
||||
0xc0, 0xd1, 0xdf, 0xc4, 0xc2, 0x21, 0x25, 0xe6, 0xd6, 0x45, 0xc2, 0x79, 0x78, 0x84,
|
||||
0x6b, 0xc4, 0x4b, 0x37,
|
||||
],
|
||||
[
|
||||
0xcf, 0xf9, 0x98, 0x0d, 0xea, 0xae, 0xb9, 0x9a, 0xab, 0x22, 0xa8, 0x66, 0x92, 0x9d,
|
||||
0xc2, 0x4d, 0x1d, 0x80, 0x56, 0x5e, 0x7a, 0x2c, 0xdf, 0x74, 0x5d, 0x0a, 0xb9, 0x67,
|
||||
0x6e, 0xdf, 0x2c, 0x0d,
|
||||
],
|
||||
[
|
||||
0x98, 0x35, 0xdf, 0xe3, 0x97, 0x53, 0xce, 0xbb, 0xdf, 0x66, 0xc6, 0x0b, 0x44, 0xed,
|
||||
0x14, 0x4a, 0xe3, 0xbb, 0x06, 0x90, 0x57, 0xe4, 0x43, 0x1d, 0x84, 0x29, 0xf3, 0x61,
|
||||
0xc6, 0x9d, 0x21, 0x0f,
|
||||
],
|
||||
[
|
||||
0xc5, 0xad, 0x66, 0x19, 0xe9, 0x6c, 0x95, 0xfd, 0xe4, 0x69, 0xf4, 0xf7, 0x20, 0x2e,
|
||||
0xb0, 0x31, 0xac, 0x95, 0xd8, 0xd2, 0x3f, 0x31, 0x03, 0x16, 0x6a, 0x44, 0x4c, 0xf1,
|
||||
0x2c, 0x4b, 0x81, 0x3f,
|
||||
],
|
||||
[
|
||||
0x8a, 0x8c, 0x06, 0x1c, 0x64, 0xea, 0x83, 0x37, 0xf8, 0x11, 0x11, 0xdd, 0x38, 0x6b,
|
||||
0xef, 0x22, 0xfe, 0x0f, 0x52, 0xe2, 0xc6, 0xa7, 0x8f, 0xbc, 0xc0, 0xd9, 0x0d, 0xcf,
|
||||
0x28, 0xb2, 0xdf, 0x1b,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0x7a, 0x3c, 0x6d, 0xc5, 0x93, 0xb9, 0x65, 0xa4, 0xa3, 0x11, 0x8a, 0x0e, 0x9e, 0x80,
|
||||
0xd0, 0x78, 0x2c, 0xb4, 0x13, 0x99, 0x79, 0x0b, 0xa7, 0xf6, 0x49, 0x77, 0x1c, 0x74,
|
||||
0x43, 0xa3, 0x6d, 0x01,
|
||||
],
|
||||
[
|
||||
0x0b, 0xbb, 0x5a, 0xfb, 0xb7, 0xc5, 0x00, 0x5e, 0x14, 0x9f, 0x8b, 0x5b, 0xf5, 0xfe,
|
||||
0x35, 0x40, 0xb3, 0x7d, 0x31, 0xe3, 0xc2, 0xb2, 0x14, 0x33, 0x73, 0x59, 0xda, 0xe3,
|
||||
0x31, 0x30, 0x23, 0x11,
|
||||
],
|
||||
[
|
||||
0x93, 0x35, 0x52, 0xa5, 0xc9, 0xd7, 0x3e, 0x50, 0xf6, 0xdb, 0xcf, 0xa4, 0x1e, 0xa3,
|
||||
0xd0, 0xa5, 0x29, 0x46, 0xb1, 0x87, 0x8e, 0xda, 0x5c, 0x96, 0xd7, 0xd8, 0xb0, 0xe4,
|
||||
0x71, 0x68, 0x0b, 0x27,
|
||||
],
|
||||
[
|
||||
0x62, 0x8b, 0xa8, 0x4e, 0x08, 0x63, 0xb6, 0x27, 0x72, 0x7d, 0xf9, 0xa1, 0x98, 0xb0,
|
||||
0x9d, 0xd0, 0xe3, 0x1a, 0xff, 0x7c, 0xba, 0xec, 0x87, 0xcd, 0x93, 0x3d, 0x0f, 0x76,
|
||||
0x58, 0xea, 0xcb, 0x3a,
|
||||
],
|
||||
[
|
||||
0x90, 0x5f, 0x7a, 0x5e, 0x88, 0xa0, 0x16, 0xa1, 0x66, 0x8d, 0xdd, 0x41, 0xea, 0xcb,
|
||||
0x91, 0x28, 0x78, 0xc7, 0xcb, 0xeb, 0x42, 0xa9, 0x31, 0x90, 0x33, 0x98, 0xe0, 0x5f,
|
||||
0xdc, 0xe6, 0x8b, 0x29,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0x94, 0x92, 0xc9, 0x01, 0x95, 0x73, 0x31, 0xd5, 0xe7, 0x94, 0xe5, 0x9a, 0xd4, 0x73,
|
||||
0x67, 0x51, 0x68, 0x0f, 0x2f, 0xde, 0xca, 0x12, 0x3f, 0xf3, 0xcb, 0xe0, 0xc6, 0x3a,
|
||||
0x2d, 0xc8, 0x61, 0x36,
|
||||
],
|
||||
[
|
||||
0x5c, 0xd8, 0x54, 0x1a, 0x4c, 0x92, 0x0e, 0x4d, 0x7e, 0xac, 0x65, 0x71, 0xa6, 0xe9,
|
||||
0x3b, 0x40, 0xcb, 0xda, 0x99, 0xf3, 0x48, 0xf1, 0x70, 0x40, 0x0f, 0x91, 0x81, 0xb8,
|
||||
0x1e, 0xb2, 0xcf, 0x17,
|
||||
],
|
||||
[
|
||||
0x65, 0x90, 0x97, 0x4e, 0xcb, 0x6e, 0x62, 0xb4, 0x8d, 0x21, 0x9f, 0xcf, 0xf7, 0x5d,
|
||||
0x6d, 0xf8, 0x55, 0x8e, 0xb6, 0xb5, 0xd4, 0x3e, 0x17, 0x3c, 0x88, 0x45, 0xb0, 0x59,
|
||||
0x31, 0x5c, 0x56, 0x10,
|
||||
],
|
||||
[
|
||||
0x0c, 0xe2, 0xec, 0x64, 0xa3, 0xd3, 0x5a, 0xca, 0x95, 0x4f, 0x9f, 0x77, 0xfc, 0xb6,
|
||||
0x96, 0xb2, 0x13, 0xb1, 0x09, 0xc9, 0xef, 0x55, 0x14, 0xf2, 0x84, 0x1a, 0xd0, 0xd9,
|
||||
0x6f, 0x49, 0x17, 0x38,
|
||||
],
|
||||
[
|
||||
0x2c, 0x02, 0x15, 0x93, 0x8c, 0x51, 0x65, 0xff, 0xd4, 0x67, 0x0d, 0xd5, 0xec, 0xff,
|
||||
0xae, 0xb2, 0x4c, 0x4c, 0x35, 0xde, 0x59, 0x4e, 0xad, 0xc8, 0xf4, 0xd4, 0xd6, 0x0e,
|
||||
0xac, 0xb9, 0xc0, 0x31,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0xe6, 0x58, 0x63, 0x2a, 0x79, 0x0b, 0x84, 0xc2, 0x78, 0x7f, 0x87, 0x33, 0x28, 0x3f,
|
||||
0xe6, 0xd2, 0x32, 0x60, 0x0d, 0x73, 0xba, 0x89, 0x79, 0x5b, 0xad, 0x40, 0x0c, 0x4b,
|
||||
0x9b, 0x77, 0xa0, 0x29,
|
||||
],
|
||||
[
|
||||
0x4e, 0x15, 0x6a, 0x3b, 0x37, 0x24, 0x5d, 0xe5, 0xd9, 0x77, 0x64, 0xe1, 0x7d, 0x8c,
|
||||
0x46, 0x46, 0x70, 0x38, 0xd8, 0xc7, 0xae, 0x5c, 0xee, 0x82, 0x25, 0xa1, 0xe2, 0xa2,
|
||||
0xad, 0x7f, 0x4a, 0x25,
|
||||
],
|
||||
[
|
||||
0xdd, 0x8e, 0x1a, 0xf4, 0x86, 0xfd, 0x57, 0x39, 0x3c, 0x41, 0xbd, 0x93, 0x83, 0xc8,
|
||||
0x76, 0x3d, 0x17, 0xfe, 0x14, 0xeb, 0xed, 0x7b, 0x0f, 0x2b, 0x08, 0x42, 0x96, 0x30,
|
||||
0x1f, 0x72, 0xbd, 0x07,
|
||||
],
|
||||
[
|
||||
0x92, 0x78, 0xa1, 0xa9, 0x69, 0x31, 0x11, 0xfa, 0x4c, 0x47, 0x00, 0x70, 0xd6, 0x2c,
|
||||
0xef, 0x52, 0x67, 0x61, 0x1f, 0xec, 0x8f, 0xeb, 0xad, 0x0e, 0x3c, 0x97, 0x73, 0xb7,
|
||||
0xef, 0x8f, 0x02, 0x1d,
|
||||
],
|
||||
[
|
||||
0x44, 0x37, 0x42, 0x53, 0xe5, 0x53, 0x0f, 0x77, 0x64, 0xfd, 0x67, 0x04, 0xe9, 0xb7,
|
||||
0xb5, 0x06, 0x4a, 0x3d, 0x5b, 0xcb, 0x52, 0x5a, 0xff, 0x1d, 0xab, 0xde, 0x9f, 0x09,
|
||||
0x1c, 0xa1, 0xb6, 0x13,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
0x80, 0x23, 0x61, 0x98, 0x92, 0x47, 0xb1, 0xb6, 0x2e, 0xd1, 0xce, 0xa5, 0x5a, 0xab,
|
||||
0x89, 0xec, 0x29, 0x87, 0xf3, 0x05, 0x2f, 0x1d, 0x1a, 0x29, 0x84, 0x60, 0x9d, 0x1f,
|
||||
0x1e, 0x80, 0xd6, 0x00,
|
||||
],
|
||||
[
|
||||
0xfa, 0xb8, 0xed, 0x98, 0x20, 0xb7, 0x9a, 0x0d, 0xe9, 0x96, 0xcd, 0x0e, 0x8f, 0x52,
|
||||
0xb2, 0x4b, 0x02, 0xec, 0x89, 0x5f, 0x3f, 0xce, 0x56, 0xa4, 0x67, 0x79, 0xb5, 0x03,
|
||||
0x09, 0x83, 0x5a, 0x23,
|
||||
],
|
||||
[
|
||||
0xd9, 0xfe, 0x9f, 0x36, 0x08, 0xa6, 0xc6, 0xff, 0x09, 0x93, 0x58, 0xa6, 0x94, 0xf6,
|
||||
0xae, 0xce, 0x4d, 0xa0, 0xc5, 0x35, 0x62, 0x8a, 0x88, 0x82, 0x43, 0x52, 0x9c, 0xa9,
|
||||
0xb1, 0xf7, 0xa8, 0x10,
|
||||
],
|
||||
[
|
||||
0x1f, 0x01, 0xf1, 0xf8, 0xf4, 0xb5, 0x64, 0xd8, 0xb0, 0xff, 0xd3, 0x2a, 0xff, 0x11,
|
||||
0x24, 0x3d, 0xee, 0x25, 0x82, 0xb4, 0xa3, 0xef, 0xbb, 0xce, 0xc9, 0x20, 0x02, 0x6c,
|
||||
0x5e, 0x1f, 0x44, 0x1e,
|
||||
],
|
||||
[
|
||||
0xa0, 0x65, 0x87, 0xcc, 0xc0, 0x3f, 0x14, 0x92, 0x94, 0x65, 0xcd, 0x3d, 0x93, 0x37,
|
||||
0x90, 0xda, 0xb1, 0x23, 0xae, 0x4e, 0x57, 0x55, 0x8a, 0x7a, 0xf4, 0xfc, 0x06, 0x1e,
|
||||
0x6b, 0x46, 0x7f, 0x1c,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
pub struct TestVector {
|
||||
pub anchor: [u8; 32],
|
||||
}
|
||||
|
||||
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_merkle_tree.py
|
||||
pub const ROOTS: [TestVector; 10] = [
|
||||
TestVector {
|
||||
anchor: [
|
||||
0xc8, 0x75, 0xbe, 0x2d, 0x60, 0x87, 0x3f, 0x8b, 0xcd, 0xeb, 0x91, 0x28, 0x2e, 0x64,
|
||||
0x2e, 0x0c, 0xc6, 0x5f, 0xf7, 0xd0, 0x64, 0x2d, 0x13, 0x7b, 0x28, 0xcf, 0x28, 0xcc,
|
||||
0x9c, 0x52, 0x7f, 0x0e,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0xf2, 0x49, 0x6f, 0x72, 0xdd, 0x17, 0xc3, 0x1b, 0xd9, 0x42, 0x83, 0x32, 0xf4, 0xc0,
|
||||
0x4f, 0x01, 0xd5, 0xd4, 0x7f, 0xc4, 0xf5, 0x8a, 0x8b, 0xa3, 0x65, 0xe8, 0x7b, 0xad,
|
||||
0x50, 0x1a, 0x46, 0x10,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0xfe, 0x29, 0x24, 0xb2, 0x64, 0x60, 0xa6, 0xd2, 0x53, 0xea, 0xba, 0xcb, 0x8b, 0xaf,
|
||||
0x24, 0x1e, 0x2f, 0x7b, 0xaf, 0x59, 0x46, 0x16, 0xd0, 0x8b, 0xb0, 0x98, 0x34, 0xd5,
|
||||
0xf1, 0xae, 0x04, 0x18,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0x3c, 0x18, 0xb6, 0x31, 0xde, 0xa5, 0x35, 0x62, 0xf1, 0xfd, 0x7a, 0xfa, 0xeb, 0x88,
|
||||
0x2d, 0x4f, 0x1f, 0xce, 0xfe, 0x2f, 0x17, 0xa0, 0xf7, 0x0e, 0xb8, 0xcc, 0xf9, 0xb7,
|
||||
0xb8, 0xfc, 0x43, 0x14,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0x44, 0x33, 0xe8, 0xfe, 0xfb, 0xc7, 0x68, 0xcf, 0x4f, 0xae, 0x51, 0x2f, 0x63, 0x97,
|
||||
0x82, 0xee, 0x6b, 0xbd, 0x83, 0x21, 0xfc, 0xd8, 0x9c, 0x5b, 0xde, 0xfb, 0x6d, 0x8f,
|
||||
0xce, 0x8d, 0xd9, 0x1b,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0xc2, 0x17, 0x65, 0x74, 0xdc, 0x6c, 0x15, 0x84, 0xa8, 0x36, 0xee, 0x58, 0x94, 0x54,
|
||||
0x26, 0xe3, 0xbd, 0x76, 0x7f, 0x7f, 0xa4, 0x90, 0xed, 0x68, 0xee, 0xdc, 0x88, 0xc2,
|
||||
0xaa, 0xdb, 0xbb, 0x21,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0x71, 0x83, 0x98, 0x6f, 0x46, 0xfc, 0x71, 0x28, 0x5e, 0x3b, 0xcf, 0x81, 0x47, 0x3d,
|
||||
0x3d, 0x2d, 0x78, 0xf3, 0x1f, 0x19, 0xa5, 0xea, 0xa3, 0xb6, 0x2f, 0xb9, 0x06, 0x20,
|
||||
0x4f, 0x2e, 0x2a, 0x31,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0xa3, 0x6e, 0xe6, 0xa9, 0x71, 0x99, 0xaa, 0x81, 0x4a, 0xb9, 0xba, 0x8c, 0xf7, 0x2e,
|
||||
0xc9, 0x88, 0x44, 0x2a, 0xc6, 0x67, 0x31, 0xcc, 0x1d, 0xb7, 0x0d, 0xeb, 0x55, 0x19,
|
||||
0x9d, 0x75, 0x4f, 0x13,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0x6e, 0xff, 0x53, 0xfe, 0x45, 0xb4, 0x0c, 0x37, 0xfe, 0x29, 0x7b, 0xf8, 0xbe, 0x34,
|
||||
0x5f, 0x33, 0xcd, 0x6c, 0xa5, 0x81, 0x07, 0x5a, 0xc8, 0xab, 0xc7, 0xfb, 0x11, 0xa8,
|
||||
0xb1, 0x68, 0x71, 0x06,
|
||||
],
|
||||
},
|
||||
TestVector {
|
||||
anchor: [
|
||||
0x15, 0x61, 0x18, 0xf7, 0xa5, 0xd8, 0xbb, 0x90, 0xb5, 0xf2, 0xd3, 0x3a, 0x71, 0xb5,
|
||||
0x15, 0xb7, 0xf4, 0xbe, 0x7a, 0x98, 0xb0, 0xff, 0x4e, 0xc4, 0x3a, 0x16, 0x70, 0x03,
|
||||
0xb0, 0x72, 0xe4, 0x34,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
use halo2::arithmetic::FieldExt;
|
||||
use halo2::pasta::pallas;
|
||||
|
||||
use crate::orchard::tests::test_vectors;
|
||||
use crate::orchard::tree::*;
|
||||
|
||||
#[test]
|
||||
fn empty_roots() {
|
||||
zebra_test::init();
|
||||
|
||||
for i in 0..EMPTY_ROOTS.len() {
|
||||
assert_eq!(
|
||||
EMPTY_ROOTS[i].to_bytes(),
|
||||
// The test vector is in reversed order.
|
||||
test_vectors::EMPTY_ROOTS[MERKLE_DEPTH - i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incremental_roots() {
|
||||
zebra_test::init();
|
||||
|
||||
let mut leaves = vec![];
|
||||
|
||||
let mut incremental_tree = NoteCommitmentTree::default();
|
||||
|
||||
for (i, commitment_set) in test_vectors::COMMITMENTS.iter().enumerate() {
|
||||
for cm_x_bytes in commitment_set.iter() {
|
||||
let cm_x = pallas::Base::from_bytes(&cm_x_bytes).unwrap();
|
||||
|
||||
leaves.push(cm_x);
|
||||
|
||||
let _ = incremental_tree.append(cm_x);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(incremental_tree.hash()),
|
||||
hex::encode(test_vectors::ROOTS[i].anchor)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
hex::encode((NoteCommitmentTree::from(leaves.clone())).hash()),
|
||||
hex::encode(test_vectors::ROOTS[i].anchor)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
//! Note Commitment Trees.
|
||||
//!
|
||||
//! A note commitment tree is an incremental Merkle tree of fixed depth
|
||||
//! used to store note commitments that JoinSplit transfers or Spend
|
||||
//! used to store note commitments that Action
|
||||
//! transfers produce. Just as the unspent transaction output set (UTXO
|
||||
//! set) used in Bitcoin, it is used to express the existence of value and
|
||||
//! the capability to spend it. However, unlike the UTXO set, it is not
|
||||
|
@ -15,7 +15,6 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
convert::TryFrom,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
|
@ -24,65 +23,65 @@ use std::{
|
|||
|
||||
use bitvec::prelude::*;
|
||||
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
||||
use incrementalmerkletree::{bridgetree, Frontier};
|
||||
use lazy_static::lazy_static;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::{commitment::NoteCommitment, sinsemilla::*};
|
||||
use super::sinsemilla::*;
|
||||
|
||||
use crate::serialization::{
|
||||
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
||||
};
|
||||
|
||||
const MERKLE_DEPTH: usize = 32;
|
||||
pub(super) const MERKLE_DEPTH: usize = 32;
|
||||
|
||||
/// MerkleCRH^Orchard Hash Function
|
||||
///
|
||||
/// Used to hash incremental Merkle tree hash values for Orchard.
|
||||
///
|
||||
/// MerkleCRH^Orchard: {0..MerkleDepth^Orchard − 1} × P𝑥 ∪ {⊥} × P𝑥 ∪ {⊥} → P𝑥 ∪ {⊥}
|
||||
/// MerkleCRH^Orchard: {0..MerkleDepth^Orchard − 1} × P𝑥 × P𝑥 → P𝑥
|
||||
///
|
||||
/// MerkleCRH^Orchard(layer, left, right) := SinsemillaHash("z.cash:Orchard-MerkleCRH", l || left || right),
|
||||
/// MerkleCRH^Orchard(layer, left, right) := 0 if hash == ⊥; hash otherwise
|
||||
///
|
||||
/// where l = I2LEBSP_10(MerkleDepth^Orchard − 1 − layer), and left, right, and
|
||||
/// where hash = SinsemillaHash("z.cash:Orchard-MerkleCRH", l || left || right),
|
||||
/// l = I2LEBSP_10(MerkleDepth^Orchard − 1 − layer), and left, right, and
|
||||
/// the output are the x-coordinates of Pallas affine points.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#constants
|
||||
fn merkle_crh_orchard(
|
||||
layer: u8,
|
||||
maybe_left: Option<pallas::Base>,
|
||||
maybe_right: Option<pallas::Base>,
|
||||
) -> Option<pallas::Base> {
|
||||
match (maybe_left, maybe_right) {
|
||||
(None, _) | (_, None) => None,
|
||||
(Some(left), Some(right)) => {
|
||||
let mut s = bitvec![Lsb0, u8;];
|
||||
fn merkle_crh_orchard(layer: u8, left: pallas::Base, right: pallas::Base) -> pallas::Base {
|
||||
let mut s = bitvec![Lsb0, u8;];
|
||||
|
||||
// Prefix: l = I2LEBSP_10(MerkleDepth^Orchard − 1 − layer)
|
||||
let l = MERKLE_DEPTH - 1 - layer as usize;
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from([l, 0])[0..10]);
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from(left.to_bytes())[0..255]);
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from(right.to_bytes())[0..255]);
|
||||
// Prefix: l = I2LEBSP_10(MerkleDepth^Orchard − 1 − layer)
|
||||
let l = MERKLE_DEPTH - 1 - layer as usize;
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from([l, 0])[0..10]);
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from(left.to_bytes())[0..255]);
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from(right.to_bytes())[0..255]);
|
||||
|
||||
sinsemilla_hash(b"z.cash:Orchard-MerkleCRH", &s)
|
||||
}
|
||||
match sinsemilla_hash(b"z.cash:Orchard-MerkleCRH", &s) {
|
||||
Some(h) => h,
|
||||
None => pallas::Base::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Orchard note commitment trees have a max depth of 32.
|
||||
/// List of "empty" Orchard note commitment nodes, one for each layer.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/nu5.pdf#constants
|
||||
static ref EMPTY_ROOTS: Vec<pallas::Base> = {
|
||||
|
||||
// The empty leaf node, Uncommitted^Orchard = I2LEBSP_l_MerkleOrchard(2)
|
||||
/// The list is indexed by the layer number (0: root; MERKLE_DEPTH: leaf).
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#constants
|
||||
pub(super) static ref EMPTY_ROOTS: Vec<pallas::Base> = {
|
||||
// The empty leaf node. This is layer 32.
|
||||
let mut v = vec![NoteCommitmentTree::uncommitted()];
|
||||
|
||||
// Starting with layer 31 (the first internal layer, after the leaves),
|
||||
// generate the empty roots up to layer 0, the root.
|
||||
for d in 0..MERKLE_DEPTH
|
||||
for layer in (0..MERKLE_DEPTH).rev()
|
||||
{
|
||||
let next = merkle_crh_orchard((MERKLE_DEPTH - 1 - d) as u8, Some(v[d]), Some(v[d])).unwrap();
|
||||
v.push(next);
|
||||
// The vector is generated from the end, pushing new nodes to its beginning.
|
||||
// For this reason, the layer below is v[0].
|
||||
let next = merkle_crh_orchard(layer as u8, v[0], v[0]);
|
||||
v.insert(0, next);
|
||||
}
|
||||
|
||||
v
|
||||
|
@ -90,12 +89,6 @@ lazy_static! {
|
|||
};
|
||||
}
|
||||
|
||||
/// The index of a note’s commitment at the leafmost layer of its
|
||||
/// `NoteCommitmentTree`.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/nu5.pdf#merkletree
|
||||
pub struct Position(pub(crate) u64);
|
||||
|
||||
/// Orchard note commitment tree root node hash.
|
||||
///
|
||||
/// The root hash in LEBS2OSP256(rt) encoding of the Orchard note commitment
|
||||
|
@ -154,70 +147,81 @@ impl ZcashDeserialize for Root {
|
|||
}
|
||||
}
|
||||
|
||||
/// Orchard Note Commitment Tree
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
struct NoteCommitmentTree {
|
||||
/// The root node of the tree (often used as an anchor).
|
||||
root: Root,
|
||||
/// The height of the tree (maximum height for Orchard is 32).
|
||||
height: u8,
|
||||
/// The number of leaves (note commitments) in this tree.
|
||||
count: u32,
|
||||
}
|
||||
/// A node of the Orchard Incremental Note Commitment Tree.
|
||||
#[derive(Clone, Debug)]
|
||||
struct Node(pallas::Base);
|
||||
|
||||
impl From<Vec<NoteCommitment>> for NoteCommitmentTree {
|
||||
fn from(_values: Vec<NoteCommitment>) -> Self {
|
||||
unimplemented!();
|
||||
impl incrementalmerkletree::Hashable for Node {
|
||||
fn empty_leaf() -> Self {
|
||||
Self(NoteCommitmentTree::uncommitted())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let layer = (MERKLE_DEPTH - 1) as u8 - 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 {
|
||||
let layer_below: usize = MERKLE_DEPTH - usize::from(level);
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<pallas::Base>> for NoteCommitmentTree {
|
||||
fn from(values: Vec<pallas::Base>) -> Self {
|
||||
if values.is_empty() {
|
||||
return NoteCommitmentTree {
|
||||
root: Root::default(),
|
||||
height: 0,
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
|
||||
let count = values.len() as u32;
|
||||
let mut height = 0u8;
|
||||
let mut current_layer: VecDeque<pallas::Base> = values.into_iter().collect();
|
||||
|
||||
while usize::from(height) < MERKLE_DEPTH {
|
||||
let mut next_layer_up = vec![];
|
||||
|
||||
while !current_layer.is_empty() {
|
||||
let left = current_layer.pop_front().unwrap();
|
||||
let right;
|
||||
if current_layer.is_empty() {
|
||||
right = EMPTY_ROOTS[height as usize];
|
||||
} else {
|
||||
right = current_layer.pop_front().unwrap();
|
||||
}
|
||||
next_layer_up.push(merkle_crh_orchard(height, Some(left), Some(right)).unwrap());
|
||||
}
|
||||
|
||||
height += 1;
|
||||
current_layer = next_layer_up.into();
|
||||
}
|
||||
|
||||
assert!(current_layer.len() == 1);
|
||||
|
||||
NoteCommitmentTree {
|
||||
root: Root(current_layer.pop_front().unwrap()),
|
||||
height,
|
||||
count,
|
||||
}
|
||||
impl From<pallas::Base> for Node {
|
||||
fn from(x: pallas::Base) -> Self {
|
||||
Node(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, missing_docs)]
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum NoteCommitmentTreeError {
|
||||
#[error("The note commitment tree is full")]
|
||||
FullTree,
|
||||
}
|
||||
|
||||
/// Orchard Incremental Note Commitment Tree
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NoteCommitmentTree {
|
||||
/// The tree represented as a Frontier.
|
||||
///
|
||||
/// A 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.
|
||||
inner: bridgetree::Frontier<Node, { MERKLE_DEPTH as u8 }>,
|
||||
}
|
||||
|
||||
impl NoteCommitmentTree {
|
||||
/// Get the Pallas-based Sinsemilla hash of root node of this merkle tree of
|
||||
/// Adds a note commitment x-coordinate to the tree.
|
||||
///
|
||||
/// The leaves of the tree are actually a base field element, the
|
||||
/// x-coordinate of the commitment, the data that is actually stored on the
|
||||
/// chain and input into the proof.
|
||||
///
|
||||
/// Returns an error if the tree is full.
|
||||
pub fn append(&mut self, cm_x: pallas::Base) -> Result<(), NoteCommitmentTreeError> {
|
||||
if self.inner.append(&cm_x.into()) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(NoteCommitmentTreeError::FullTree)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current root of the tree, used as an anchor in Orchard
|
||||
/// shielded transactions.
|
||||
pub fn root(&self) -> Root {
|
||||
Root(self.inner.root().0)
|
||||
}
|
||||
|
||||
/// Get the Pallas-based Sinsemilla hash / root node of this merkle tree of
|
||||
/// note commitments.
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
self.root.into()
|
||||
self.root().into()
|
||||
}
|
||||
|
||||
/// An as-yet unused Orchard note commitment tree leaf node.
|
||||
|
@ -228,22 +232,46 @@ impl NoteCommitmentTree {
|
|||
pub fn uncommitted() -> pallas::Base {
|
||||
pallas::Base::one().double()
|
||||
}
|
||||
|
||||
/// Count of note commitments added to the tree.
|
||||
///
|
||||
/// For Orchard, the tree is capped at 2^32.
|
||||
pub fn count(&self) -> u64 {
|
||||
self.inner
|
||||
.position()
|
||||
.map_or(0, |pos| usize::from(pos) as u64 + 1)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check empty roots, incremental roots, as part of https://github.com/ZcashFoundation/zebra/issues/1287
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::orchard::tests::test_vectors;
|
||||
|
||||
#[test]
|
||||
fn empty_roots() {
|
||||
zebra_test::init();
|
||||
|
||||
for i in 0..EMPTY_ROOTS.len() {
|
||||
assert_eq!(EMPTY_ROOTS[i].to_bytes(), test_vectors::EMPTY_ROOTS[i]);
|
||||
impl Default for NoteCommitmentTree {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: bridgetree::Frontier::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for NoteCommitmentTree {}
|
||||
|
||||
impl PartialEq for NoteCommitmentTree {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hash() == other.hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<pallas::Base>> for NoteCommitmentTree {
|
||||
/// Compute the tree from a whole bunch of note commitments at once.
|
||||
fn from(values: Vec<pallas::Base>) -> Self {
|
||||
let mut tree = Self::default();
|
||||
|
||||
if values.is_empty() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
for cm_x in values {
|
||||
let _ = tree.append(cm_x);
|
||||
}
|
||||
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
mod preallocate;
|
||||
mod prop;
|
||||
mod test_vectors;
|
||||
mod tree;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// From https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/merkle_tree.rs#L512
|
||||
pub const HEX_EMPTY_ROOTS: [&str; 33] = [
|
||||
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||
"817de36ab2d57feb077634bca77819c8e0bd298c04f6fed0e6a83cc1356ca155",
|
||||
"ffe9fc03f18b176c998806439ff0bb8ad193afdb27b2ccbc88856916dd804e34",
|
||||
"d8283386ef2ef07ebdbb4383c12a739a953a4d6e0d6fb1139a4036d693bfbb6c",
|
||||
"e110de65c907b9dea4ae0bd83a4b0a51bea175646a64c12b4c9f931b2cb31b49",
|
||||
"912d82b2c2bca231f71efcf61737fbf0a08befa0416215aeef53e8bb6d23390a",
|
||||
"8ac9cf9c391e3fd42891d27238a81a8a5c1d3a72b1bcbea8cf44a58ce7389613",
|
||||
"d6c639ac24b46bd19341c91b13fdcab31581ddaf7f1411336a271f3d0aa52813",
|
||||
"7b99abdc3730991cc9274727d7d82d28cb794edbc7034b4f0053ff7c4b680444",
|
||||
"43ff5457f13b926b61df552d4e402ee6dc1463f99a535f9a713439264d5b616b",
|
||||
"ba49b659fbd0b7334211ea6a9d9df185c757e70aa81da562fb912b84f49bce72",
|
||||
"4777c8776a3b1e69b73a62fa701fa4f7a6282d9aee2c7a6b82e7937d7081c23c",
|
||||
"ec677114c27206f5debc1c1ed66f95e2b1885da5b7be3d736b1de98579473048",
|
||||
"1b77dac4d24fb7258c3c528704c59430b630718bec486421837021cf75dab651",
|
||||
"bd74b25aacb92378a871bf27d225cfc26baca344a1ea35fdd94510f3d157082c",
|
||||
"d6acdedf95f608e09fa53fb43dcd0990475726c5131210c9e5caeab97f0e642f",
|
||||
"1ea6675f9551eeb9dfaaa9247bc9858270d3d3a4c5afa7177a984d5ed1be2451",
|
||||
"6edb16d01907b759977d7650dad7e3ec049af1a3d875380b697c862c9ec5d51c",
|
||||
"cd1c8dbf6e3acc7a80439bc4962cf25b9dce7c896f3a5bd70803fc5a0e33cf00",
|
||||
"6aca8448d8263e547d5ff2950e2ed3839e998d31cbc6ac9fd57bc6002b159216",
|
||||
"8d5fa43e5a10d11605ac7430ba1f5d81fb1b68d29a640405767749e841527673",
|
||||
"08eeab0c13abd6069e6310197bf80f9c1ea6de78fd19cbae24d4a520e6cf3023",
|
||||
"0769557bc682b1bf308646fd0b22e648e8b9e98f57e29f5af40f6edb833e2c49",
|
||||
"4c6937d78f42685f84b43ad3b7b00f81285662f85c6a68ef11d62ad1a3ee0850",
|
||||
"fee0e52802cb0c46b1eb4d376c62697f4759f6c8917fa352571202fd778fd712",
|
||||
"16d6252968971a83da8521d65382e61f0176646d771c91528e3276ee45383e4a",
|
||||
"d2e1642c9a462229289e5b0e3b7f9008e0301cbb93385ee0e21da2545073cb58",
|
||||
"a5122c08ff9c161d9ca6fc462073396c7d7d38e8ee48cdb3bea7e2230134ed6a",
|
||||
"28e7b841dcbc47cceb69d7cb8d94245fb7cb2ba3a7a6bc18f13f945f7dbd6e2a",
|
||||
"e1f34b034d4a3cd28557e2907ebf990c918f64ecb50a94f01d6fda5ca5c7ef72",
|
||||
"12935f14b676509b81eb49ef25f39269ed72309238b4c145803544b646dca62d",
|
||||
"b2eed031d4d6a4f02a097f80b54cc1541d4163c6b6f5971f88b6e41d35c53814",
|
||||
"fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
|
||||
];
|
||||
|
||||
// From https://github.com/zcash/zcash/blob/master/src/test/data/merkle_commitments_sapling.json
|
||||
// Byte-reversed from those ones because the original test vectors are loaded using uint256S()
|
||||
pub const COMMITMENTS: [&str; 16] = [
|
||||
"b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55",
|
||||
"225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b11458",
|
||||
"7c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e036c",
|
||||
"50421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a030",
|
||||
"aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc12",
|
||||
"f76748d40d5ee5f9a608512e7954dd515f86e8f6d009141c89163de1cf351a02",
|
||||
"bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0b2e",
|
||||
"da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a511",
|
||||
"3a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f77446",
|
||||
"c7ca8f7df8fd997931d33985d935ee2d696856cc09cc516d419ea6365f163008",
|
||||
"f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c3702",
|
||||
"e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c608",
|
||||
"8cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e826",
|
||||
"22fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c03",
|
||||
"f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c",
|
||||
"3a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac15",
|
||||
];
|
||||
|
||||
// Calculated by modifying TestCommitmentTree in
|
||||
// https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/merkle_tree.rs
|
||||
// to compute the full Sapling height root (32).
|
||||
pub const ROOTS: [&str; 16] = [
|
||||
"ee880ed73e96ba0739578c87ba8e6a4bc33b5e63bb98875e6e2f04b214e9fb59",
|
||||
"321aef631f1a9b7914d40d7bab34c29145ac6cf69d24bf0fc566b33ac9029972",
|
||||
"ddaa1ab86de5c153993414f34ba97e9674c459dfadde112b89eeeafa0e5a204c",
|
||||
"0b337c75535b09468955d499e37cb7e2466f1f0c861ddea929aa13c699c1a454",
|
||||
"5a9b9764d76a45848012eec306d6f6bface319ad5d9bf88db96b3b19edded716",
|
||||
"004075c72e360d7b2ab113555e97dcf4fb50f211d74841eafb05aaff705e3235",
|
||||
"ebf2139c2ef10d51f21fee18521963b91b64987f2743d908be2b80b4ae29e622",
|
||||
"70d07f5662eafaf054327899abce515b1c1cbac6600edea86297c2800e806534",
|
||||
"f72dad9cd0f4d4783444f6dc64d9be2edc74cffddcb60bf244e56eada508c22a",
|
||||
"7635d357c7755c91ea4d6b53e8fd42756329118577fe8b9ade3d33b316fa4948",
|
||||
"fca0c26ce07fc7e563b031d9187f829fa41715f193f08bd0ac25e5122ac75c2e",
|
||||
"0b727c9c6f66c3c749ef9c1df6c5356db8adf80fcc3c1d7fdf56b82cb8d47a3c",
|
||||
"d77d030ed3c2521567eae9555b95eca89442b0c263b82fea4359f802e0f31668",
|
||||
"3d84c8b65e5a8036d115161bb6e3ca2a556e42d376abc3d74a16bc22685b7d61",
|
||||
"84f752458538a24483e9731e32fa95cabf56aebbbc6bff8475f45299bcdcba35",
|
||||
"bb3cc8f85773c05f3332a25cc8281a68450a90807cef859b49b2f1d9d2d3a64d",
|
||||
];
|
|
@ -0,0 +1,43 @@
|
|||
use hex::FromHex;
|
||||
|
||||
use crate::sapling::tests::test_vectors;
|
||||
use crate::sapling::tree::*;
|
||||
|
||||
#[test]
|
||||
fn empty_roots() {
|
||||
zebra_test::init();
|
||||
|
||||
for i in 0..EMPTY_ROOTS.len() {
|
||||
assert_eq!(
|
||||
hex::encode(EMPTY_ROOTS[i]),
|
||||
// The test vector is in reversed order.
|
||||
test_vectors::HEX_EMPTY_ROOTS[MERKLE_DEPTH - i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incremental_roots() {
|
||||
zebra_test::init();
|
||||
|
||||
let mut leaves = vec![];
|
||||
|
||||
let mut incremental_tree = NoteCommitmentTree::default();
|
||||
|
||||
for (i, cm_u) in test_vectors::COMMITMENTS.iter().enumerate() {
|
||||
let bytes = <[u8; 32]>::from_hex(cm_u).unwrap();
|
||||
|
||||
let cm_u = jubjub::Fq::from_bytes(&bytes).unwrap();
|
||||
|
||||
leaves.push(cm_u);
|
||||
|
||||
let _ = incremental_tree.append(cm_u);
|
||||
|
||||
assert_eq!(hex::encode(incremental_tree.hash()), test_vectors::ROOTS[i]);
|
||||
|
||||
assert_eq!(
|
||||
hex::encode((NoteCommitmentTree::from(leaves.clone())).hash()),
|
||||
test_vectors::ROOTS[i]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,13 +13,16 @@
|
|||
#![allow(clippy::unit_arg)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::{collections::VecDeque, fmt};
|
||||
use std::fmt;
|
||||
|
||||
use super::commitment::{pedersen_hashes::pedersen_hash, NoteCommitment};
|
||||
use bitvec::prelude::*;
|
||||
use incrementalmerkletree::{bridgetree, Frontier};
|
||||
use lazy_static::lazy_static;
|
||||
use thiserror::Error;
|
||||
|
||||
const MERKLE_DEPTH: usize = 32;
|
||||
use super::commitment::pedersen_hashes::pedersen_hash;
|
||||
|
||||
pub(super) const MERKLE_DEPTH: usize = 32;
|
||||
|
||||
/// MerkleCRH^Sapling Hash Function
|
||||
///
|
||||
|
@ -34,7 +37,8 @@ fn merkle_crh_sapling(layer: u8, left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
|
|||
let mut s = bitvec![Lsb0, u8;];
|
||||
|
||||
// Prefix: l = I2LEBSP_6(MerkleDepth^Sapling − 1 − layer)
|
||||
s.extend_from_bitslice(&BitSlice::<Lsb0, _>::from_element(&layer)[0..6]);
|
||||
let l = (MERKLE_DEPTH - 1) as u8 - layer;
|
||||
s.extend_from_bitslice(&BitSlice::<Lsb0, _>::from_element(&l)[0..6]);
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from(left)[0..255]);
|
||||
s.extend_from_bitslice(&BitArray::<Lsb0, _>::from(right)[0..255]);
|
||||
|
||||
|
@ -42,16 +46,22 @@ fn merkle_crh_sapling(layer: u8, left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
|
|||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Sapling note commitment trees have a max depth of 32.
|
||||
/// List of "empty" Sapling note commitment nodes, one for each layer.
|
||||
///
|
||||
/// The list is indexed by the layer number (0: root; MERKLE_DEPTH: leaf).
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#constants
|
||||
static ref EMPTY_ROOTS: Vec<[u8; 32]> = {
|
||||
// Uncommitted^Sapling = I2LEBSP_l_MerkleSapling(1)
|
||||
let mut v = vec![jubjub::Fq::one().to_bytes()];
|
||||
pub(super) static ref EMPTY_ROOTS: Vec<[u8; 32]> = {
|
||||
// The empty leaf node. This is layer 32.
|
||||
let mut v = vec![NoteCommitmentTree::uncommitted()];
|
||||
|
||||
for d in 0..MERKLE_DEPTH {
|
||||
let next = merkle_crh_sapling(d as u8, v[d], v[d]);
|
||||
v.push(next);
|
||||
// Starting with layer 31 (the first internal layer, after the leaves),
|
||||
// generate the empty roots up to layer 0, the root.
|
||||
for layer in (0..MERKLE_DEPTH).rev() {
|
||||
// The vector is generated from the end, pushing new nodes to its beginning.
|
||||
// For this reason, the layer below is v[0].
|
||||
let next = merkle_crh_sapling(layer as u8, v[0], v[0]);
|
||||
v.insert(0, next);
|
||||
}
|
||||
|
||||
v
|
||||
|
@ -104,183 +114,134 @@ impl From<&Root> for [u8; 32] {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sapling Note Commitment Tree
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
struct NoteCommitmentTree {
|
||||
/// The root node of the tree (often used as an anchor).
|
||||
root: Root,
|
||||
/// The height of the tree (maximum height for Sapling is 32).
|
||||
height: u8,
|
||||
/// The number of leaves (note commitments) in this tree.
|
||||
count: u32,
|
||||
/// A node of the Sapling Incremental Note Commitment Tree.
|
||||
///
|
||||
/// 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(Clone, Debug)]
|
||||
struct Node([u8; 32]);
|
||||
|
||||
impl incrementalmerkletree::Hashable for Node {
|
||||
fn empty_leaf() -> Self {
|
||||
Self(NoteCommitmentTree::uncommitted())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let layer = (MERKLE_DEPTH - 1) as u8 - 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 {
|
||||
let layer_below: usize = MERKLE_DEPTH - usize::from(level);
|
||||
Self(EMPTY_ROOTS[layer_below])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<NoteCommitment>> for NoteCommitmentTree {
|
||||
fn from(_values: Vec<NoteCommitment>) -> Self {
|
||||
unimplemented!();
|
||||
impl From<jubjub::Fq> for Node {
|
||||
fn from(x: jubjub::Fq) -> Self {
|
||||
Node(x.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, missing_docs)]
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum NoteCommitmentTreeError {
|
||||
#[error("The note commitment tree is full")]
|
||||
FullTree,
|
||||
}
|
||||
|
||||
/// Sapling Incremental Note Commitment Tree.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NoteCommitmentTree {
|
||||
/// The tree represented as a Frontier.
|
||||
///
|
||||
/// A 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.
|
||||
inner: bridgetree::Frontier<Node, { MERKLE_DEPTH as u8 }>,
|
||||
}
|
||||
|
||||
impl NoteCommitmentTree {
|
||||
/// Adds a note commitment u-coordinate to the tree.
|
||||
///
|
||||
/// The leaves of the tree are actually a base field element, the
|
||||
/// u-coordinate of the commitment, the data that is actually stored on the
|
||||
/// chain and input into the proof.
|
||||
///
|
||||
/// Returns an error if the tree is full.
|
||||
pub fn append(&mut self, cm_u: jubjub::Fq) -> Result<(), NoteCommitmentTreeError> {
|
||||
if self.inner.append(&cm_u.into()) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(NoteCommitmentTreeError::FullTree)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current root of the tree, used as an anchor in Sapling
|
||||
/// shielded transactions.
|
||||
pub fn root(&self) -> Root {
|
||||
Root(self.inner.root().0)
|
||||
}
|
||||
|
||||
/// Get the Jubjub-based Pedersen hash of root node of this merkle tree of
|
||||
/// note commitments.
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
self.root().into()
|
||||
}
|
||||
|
||||
/// An as-yet unused Sapling note commitment tree leaf node.
|
||||
///
|
||||
/// Distinct for Sapling, a distinguished hash value of:
|
||||
///
|
||||
/// Uncommitted^Sapling = I2LEBSP_l_MerkleSapling(1)
|
||||
pub fn uncommitted() -> [u8; 32] {
|
||||
jubjub::Fq::one().to_bytes()
|
||||
}
|
||||
|
||||
/// Count of note commitments added to the tree.
|
||||
///
|
||||
/// For Sapling, the tree is capped at 2^32.
|
||||
pub fn count(&self) -> u64 {
|
||||
self.inner
|
||||
.position()
|
||||
.map_or(0, |pos| usize::from(pos) as u64 + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NoteCommitmentTree {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: bridgetree::Frontier::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for NoteCommitmentTree {}
|
||||
|
||||
impl PartialEq for NoteCommitmentTree {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hash() == other.hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<jubjub::Fq>> for NoteCommitmentTree {
|
||||
/// Compute the tree from a whole bunch of note commitments at once.
|
||||
fn from(values: Vec<jubjub::Fq>) -> Self {
|
||||
let mut tree = Self::default();
|
||||
|
||||
if values.is_empty() {
|
||||
return NoteCommitmentTree {
|
||||
root: Root::default(),
|
||||
height: 0,
|
||||
count: 0,
|
||||
};
|
||||
return tree;
|
||||
}
|
||||
|
||||
let count = values.len() as u32;
|
||||
let mut height = 0u8;
|
||||
let mut current_layer: VecDeque<[u8; 32]> =
|
||||
values.into_iter().map(|cm_u| cm_u.to_bytes()).collect();
|
||||
|
||||
while usize::from(height) < MERKLE_DEPTH {
|
||||
let mut next_layer_up = vec![];
|
||||
|
||||
while !current_layer.is_empty() {
|
||||
let left = current_layer.pop_front().unwrap();
|
||||
let right;
|
||||
if current_layer.is_empty() {
|
||||
right = EMPTY_ROOTS[height as usize];
|
||||
} else {
|
||||
right = current_layer.pop_front().unwrap();
|
||||
}
|
||||
next_layer_up.push(merkle_crh_sapling(height, left, right));
|
||||
}
|
||||
|
||||
height += 1;
|
||||
current_layer = next_layer_up.into();
|
||||
for cm_u in values {
|
||||
let _ = tree.append(cm_u);
|
||||
}
|
||||
|
||||
assert!(current_layer.len() == 1);
|
||||
|
||||
NoteCommitmentTree {
|
||||
root: Root(current_layer.pop_front().unwrap()),
|
||||
height,
|
||||
count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoteCommitmentTree {
|
||||
/// Get the Jubjub-based Pedersen hash of root node of this merkle tree of
|
||||
/// commitment notes.
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
self.root.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use hex::FromHex;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty_roots() {
|
||||
zebra_test::init();
|
||||
|
||||
// From https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/merkle_tree.rs#L512
|
||||
const HEX_EMPTY_ROOTS: [&str; 33] = [
|
||||
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||
"817de36ab2d57feb077634bca77819c8e0bd298c04f6fed0e6a83cc1356ca155",
|
||||
"ffe9fc03f18b176c998806439ff0bb8ad193afdb27b2ccbc88856916dd804e34",
|
||||
"d8283386ef2ef07ebdbb4383c12a739a953a4d6e0d6fb1139a4036d693bfbb6c",
|
||||
"e110de65c907b9dea4ae0bd83a4b0a51bea175646a64c12b4c9f931b2cb31b49",
|
||||
"912d82b2c2bca231f71efcf61737fbf0a08befa0416215aeef53e8bb6d23390a",
|
||||
"8ac9cf9c391e3fd42891d27238a81a8a5c1d3a72b1bcbea8cf44a58ce7389613",
|
||||
"d6c639ac24b46bd19341c91b13fdcab31581ddaf7f1411336a271f3d0aa52813",
|
||||
"7b99abdc3730991cc9274727d7d82d28cb794edbc7034b4f0053ff7c4b680444",
|
||||
"43ff5457f13b926b61df552d4e402ee6dc1463f99a535f9a713439264d5b616b",
|
||||
"ba49b659fbd0b7334211ea6a9d9df185c757e70aa81da562fb912b84f49bce72",
|
||||
"4777c8776a3b1e69b73a62fa701fa4f7a6282d9aee2c7a6b82e7937d7081c23c",
|
||||
"ec677114c27206f5debc1c1ed66f95e2b1885da5b7be3d736b1de98579473048",
|
||||
"1b77dac4d24fb7258c3c528704c59430b630718bec486421837021cf75dab651",
|
||||
"bd74b25aacb92378a871bf27d225cfc26baca344a1ea35fdd94510f3d157082c",
|
||||
"d6acdedf95f608e09fa53fb43dcd0990475726c5131210c9e5caeab97f0e642f",
|
||||
"1ea6675f9551eeb9dfaaa9247bc9858270d3d3a4c5afa7177a984d5ed1be2451",
|
||||
"6edb16d01907b759977d7650dad7e3ec049af1a3d875380b697c862c9ec5d51c",
|
||||
"cd1c8dbf6e3acc7a80439bc4962cf25b9dce7c896f3a5bd70803fc5a0e33cf00",
|
||||
"6aca8448d8263e547d5ff2950e2ed3839e998d31cbc6ac9fd57bc6002b159216",
|
||||
"8d5fa43e5a10d11605ac7430ba1f5d81fb1b68d29a640405767749e841527673",
|
||||
"08eeab0c13abd6069e6310197bf80f9c1ea6de78fd19cbae24d4a520e6cf3023",
|
||||
"0769557bc682b1bf308646fd0b22e648e8b9e98f57e29f5af40f6edb833e2c49",
|
||||
"4c6937d78f42685f84b43ad3b7b00f81285662f85c6a68ef11d62ad1a3ee0850",
|
||||
"fee0e52802cb0c46b1eb4d376c62697f4759f6c8917fa352571202fd778fd712",
|
||||
"16d6252968971a83da8521d65382e61f0176646d771c91528e3276ee45383e4a",
|
||||
"d2e1642c9a462229289e5b0e3b7f9008e0301cbb93385ee0e21da2545073cb58",
|
||||
"a5122c08ff9c161d9ca6fc462073396c7d7d38e8ee48cdb3bea7e2230134ed6a",
|
||||
"28e7b841dcbc47cceb69d7cb8d94245fb7cb2ba3a7a6bc18f13f945f7dbd6e2a",
|
||||
"e1f34b034d4a3cd28557e2907ebf990c918f64ecb50a94f01d6fda5ca5c7ef72",
|
||||
"12935f14b676509b81eb49ef25f39269ed72309238b4c145803544b646dca62d",
|
||||
"b2eed031d4d6a4f02a097f80b54cc1541d4163c6b6f5971f88b6e41d35c53814",
|
||||
"fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
|
||||
];
|
||||
|
||||
for i in 0..EMPTY_ROOTS.len() {
|
||||
assert_eq!(hex::encode(EMPTY_ROOTS[i]), HEX_EMPTY_ROOTS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incremental_roots() {
|
||||
zebra_test::init();
|
||||
// From https://github.com/zcash/zcash/blob/master/src/test/data/merkle_commitments_sapling.json
|
||||
// Byte-reversed from those ones because the original test vectors are loaded using uint256S()
|
||||
let commitments = [
|
||||
"b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55",
|
||||
"225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b11458",
|
||||
"7c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e036c",
|
||||
"50421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a030",
|
||||
"aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc12",
|
||||
"f76748d40d5ee5f9a608512e7954dd515f86e8f6d009141c89163de1cf351a02",
|
||||
"bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0b2e",
|
||||
"da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a511",
|
||||
"3a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f77446",
|
||||
"c7ca8f7df8fd997931d33985d935ee2d696856cc09cc516d419ea6365f163008",
|
||||
"f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c3702",
|
||||
"e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c608",
|
||||
"8cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e826",
|
||||
"22fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c03",
|
||||
"f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c",
|
||||
"3a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac15",
|
||||
];
|
||||
|
||||
// Calculated by modifying TestCommitmentTree in
|
||||
// https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/merkle_tree.rs
|
||||
// to compute the full Sapling height root (32).
|
||||
let roots = [
|
||||
"ee880ed73e96ba0739578c87ba8e6a4bc33b5e63bb98875e6e2f04b214e9fb59",
|
||||
"321aef631f1a9b7914d40d7bab34c29145ac6cf69d24bf0fc566b33ac9029972",
|
||||
"ddaa1ab86de5c153993414f34ba97e9674c459dfadde112b89eeeafa0e5a204c",
|
||||
"0b337c75535b09468955d499e37cb7e2466f1f0c861ddea929aa13c699c1a454",
|
||||
"5a9b9764d76a45848012eec306d6f6bface319ad5d9bf88db96b3b19edded716",
|
||||
"004075c72e360d7b2ab113555e97dcf4fb50f211d74841eafb05aaff705e3235",
|
||||
"ebf2139c2ef10d51f21fee18521963b91b64987f2743d908be2b80b4ae29e622",
|
||||
"70d07f5662eafaf054327899abce515b1c1cbac6600edea86297c2800e806534",
|
||||
"f72dad9cd0f4d4783444f6dc64d9be2edc74cffddcb60bf244e56eada508c22a",
|
||||
"7635d357c7755c91ea4d6b53e8fd42756329118577fe8b9ade3d33b316fa4948",
|
||||
"fca0c26ce07fc7e563b031d9187f829fa41715f193f08bd0ac25e5122ac75c2e",
|
||||
"0b727c9c6f66c3c749ef9c1df6c5356db8adf80fcc3c1d7fdf56b82cb8d47a3c",
|
||||
"d77d030ed3c2521567eae9555b95eca89442b0c263b82fea4359f802e0f31668",
|
||||
"3d84c8b65e5a8036d115161bb6e3ca2a556e42d376abc3d74a16bc22685b7d61",
|
||||
"84f752458538a24483e9731e32fa95cabf56aebbbc6bff8475f45299bcdcba35",
|
||||
"bb3cc8f85773c05f3332a25cc8281a68450a90807cef859b49b2f1d9d2d3a64d",
|
||||
];
|
||||
|
||||
let mut leaves = vec![];
|
||||
|
||||
for (i, cm_u) in commitments.iter().enumerate() {
|
||||
let bytes = <[u8; 32]>::from_hex(cm_u).unwrap();
|
||||
|
||||
leaves.push(jubjub::Fq::from_bytes(&bytes).unwrap());
|
||||
|
||||
let tree = NoteCommitmentTree::from(leaves.clone());
|
||||
|
||||
assert_eq!(hex::encode(tree.hash()), roots[i]);
|
||||
}
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue