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:
Deirdre Connolly 2021-07-15 03:58:36 -10:00 committed by GitHub
parent 12c60b5868
commit 48cf52735c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 807 additions and 283 deletions

10
Cargo.lock generated
View File

@ -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",

View File

@ -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 }

View File

@ -1,3 +1,4 @@
mod preallocate;
mod prop;
pub mod test_vectors;
mod test_vectors;
mod tree;

View File

@ -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,
],
},
];

View File

@ -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)
);
}
}

View File

@ -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 notes 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
}
}

View File

@ -1,2 +1,4 @@
mod preallocate;
mod prop;
mod test_vectors;
mod tree;

View File

@ -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",
];

View File

@ -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]
);
}
}

View File

@ -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
}
}