From 9ea0427678797eb7e1f8692de5bd368e4cd68943 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Wed, 4 Dec 2019 13:24:26 -0800 Subject: [PATCH] MMR trees API (#118) The main design goals of this mmr implementation were 1. Avoid database callbacks. As it is implemented, calling side must just smartly pre-load MMR nodes from the database (about log2(tree length) for append, twice as much for deletion). 2. Reuse as much code/logic between rust and c++ clients. 3. Close to zero memory consumption. --- Cargo.lock | 28 +++ librustzcash/Cargo.toml | 1 + librustzcash/include/librustzcash.h | 27 +++ librustzcash/src/rustzcash.rs | 195 ++++++++++++++++++++ librustzcash/src/tests/mmr.rs | 228 ++++++++++++++++++++++++ librustzcash/src/tests/mod.rs | 1 + librustzcash/src/tests/res/tree1023.dat | Bin 0 -> 153867 bytes librustzcash/src/tests/res/tree16.dat | Bin 0 -> 2352 bytes 8 files changed, 480 insertions(+) create mode 100644 librustzcash/src/tests/mmr.rs create mode 100644 librustzcash/src/tests/res/tree1023.dat create mode 100644 librustzcash/src/tests/res/tree16.dat diff --git a/Cargo.lock b/Cargo.lock index 6b472c971..9a09a9d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,15 @@ dependencies = [ "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bigint" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bit-vec" version = "0.4.4" @@ -219,6 +228,11 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crunchy" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crypto_api" version = "0.2.2" @@ -372,6 +386,7 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.15.0", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_mmr 0.1.0 (git+https://github.com/nikvolf/zcash-mmr)", "zcash_primitives 0.1.0", "zcash_proofs 0.1.0", ] @@ -666,6 +681,16 @@ dependencies = [ "zcash_primitives 0.1.0", ] +[[package]] +name = "zcash_mmr" +version = "0.1.0" +source = "git+https://github.com/nikvolf/zcash-mmr#26be46573ee7e0094f19d861fd29793adda6298e" +dependencies = [ + "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "zcash_primitives" version = "0.1.0" @@ -713,6 +738,7 @@ dependencies = [ "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" "checksum bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2" +"checksum bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" "checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" "checksum blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea" @@ -731,6 +757,7 @@ dependencies = [ "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" "checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" @@ -781,3 +808,4 @@ dependencies = [ "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum zcash_mmr 0.1.0 (git+https://github.com/nikvolf/zcash-mmr)" = "" diff --git a/librustzcash/Cargo.toml b/librustzcash/Cargo.toml index 8f338961a..64999f23f 100644 --- a/librustzcash/Cargo.toml +++ b/librustzcash/Cargo.toml @@ -28,6 +28,7 @@ libc = "0.2" pairing = { version = "0.15.0", path = "../pairing" } lazy_static = "1" rand_core = "0.5.1" +zcash_mmr = { git = "https://github.com/nikvolf/zcash-mmr" } zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" } zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" } diff --git a/librustzcash/include/librustzcash.h b/librustzcash/include/librustzcash.h index a3ca5e8d3..1aa85ed20 100644 --- a/librustzcash/include/librustzcash.h +++ b/librustzcash/include/librustzcash.h @@ -307,6 +307,33 @@ extern "C" { unsigned char *j_ret, unsigned char *addr_ret ); + + uint32_t librustzcash_mmr_append( + uint32_t cbranch, + uint32_t t_len, + const uint32_t *ni_ptr, + const unsigned char *n_ptr, + size_t p_len, + const unsigned char *nn_ptr, + unsigned char *rt_ret, + unsigned char *buf_ret + ); + + uint32_t librustzcash_mmr_delete( + uint32_t cbranch, + uint32_t t_len, + const uint32_t *ni_ptr, + const unsigned char *n_ptr, + size_t p_len, + size_t e_len, + unsigned char *rt_ret + ); + + uint32_t librustzcash_mmr_hash_node( + uint32_t cbranch, + const unsigned char *n_ptr, + unsigned char *h_ret + ); } #endif // LIBRUSTZCASH_INCLUDE_H_ diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 9c199dddb..0bfd5adff 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -65,6 +65,8 @@ use zcash_proofs::{ sprout, }; +use zcash_mmr::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree}; + #[cfg(test)] mod tests; @@ -1173,3 +1175,196 @@ pub extern "C" fn librustzcash_zip32_xfvk_address( true } + +fn construct_mmr_tree( + // Consensus branch id + cbranch: u32, + // Length of tree in array representation + t_len: u32, + + // Indices of provided tree nodes, length of p_len+e_len + ni_ptr: *const u32, + // Provided tree nodes data, length of p_len+e_len + n_ptr: *const [c_uchar; zcash_mmr::MAX_ENTRY_SIZE], + + // Peaks count + p_len: size_t, + // Extra nodes loaded (for deletion) count + e_len: size_t, +) -> Result { + let (indices, nodes) = unsafe { + ( + slice::from_raw_parts(ni_ptr, p_len + e_len), + slice::from_raw_parts(n_ptr, p_len + e_len), + ) + }; + + let mut peaks = Vec::new(); + for i in 0..p_len { + peaks.push(( + indices[i], + match MMREntry::from_bytes(cbranch, &nodes[i][..]) { + Ok(entry) => entry, + _ => { + return Err("Invalid encoding"); + } // error + }, + )); + } + + let mut extra = Vec::new(); + for i in p_len..(p_len + e_len) { + extra.push(( + indices[i], + match MMREntry::from_bytes(cbranch, &nodes[i][..]) { + Ok(entry) => entry, + _ => { + return Err("Invalid encoding"); + } // error + }, + )); + } + + Ok(MMRTree::new(t_len, peaks, extra)) +} + +#[no_mangle] +pub extern "system" fn librustzcash_mmr_append( + // Consensus branch id + cbranch: u32, + // Length of tree in array representation + t_len: u32, + // Indices of provided tree nodes, length of p_len + ni_ptr: *const u32, + // Provided tree nodes data, length of p_len + n_ptr: *const [c_uchar; zcash_mmr::MAX_ENTRY_SIZE], + // Peaks count + p_len: size_t, + // New node pointer + nn_ptr: *const [u8; zcash_mmr::MAX_NODE_DATA_SIZE], + // Return of root commitment (32 byte hash) + rt_ret: *mut u8, + // Return buffer for appended leaves, should be pre-allocated of log2(t_len)+1 length + buf_ret: *mut [c_uchar; zcash_mmr::MAX_NODE_DATA_SIZE], +) -> u32 { + let new_node_bytes: &[u8; zcash_mmr::MAX_NODE_DATA_SIZE] = unsafe { + match nn_ptr.as_ref() { + Some(r) => r, + None => { + return 0; + } // Null pointer passed, error + } + }; + + let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, 0) { + Ok(t) => t, + _ => { + return 0; + } // error + }; + + let node = match MMRNodeData::from_bytes(cbranch, &new_node_bytes[..]) { + Ok(node) => node, + _ => { + return 0; + } // error + }; + + let appended = match tree.append_leaf(node) { + Ok(appended) => appended, + _ => { + return 0; + } + }; + + let return_count = appended.len(); + + let root_node = tree + .root_node() + .expect("Just added, should resolve always; qed"); + unsafe { + slice::from_raw_parts_mut(rt_ret, 32).copy_from_slice(&root_node.data().subtree_commitment); + + for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count as usize) + .iter_mut() + .enumerate() + { + tree.resolve_link(appended[idx]) + .expect("This was generated by the tree and thus resolvable; qed") + .data() + .write(&mut &mut next_buf[..]) + .expect("Write using cursor with enough buffer size cannot fail; qed"); + } + } + + return_count as u32 +} + +#[no_mangle] +pub extern "system" fn librustzcash_mmr_delete( + // Consensus branch id + cbranch: u32, + // Length of tree in array representation + t_len: u32, + // Indices of provided tree nodes, length of p_len+e_len + ni_ptr: *const u32, + // Provided tree nodes data, length of p_len+e_len + n_ptr: *const [c_uchar; zcash_mmr::MAX_ENTRY_SIZE], + // Peaks count + p_len: size_t, + // Extra nodes loaded (for deletion) count + e_len: size_t, + // Return of root commitment (32 byte hash) + rt_ret: *mut u8, +) -> u32 { + let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len) { + Ok(t) => t, + _ => { + return 0; + } // error + }; + + let truncate_len = match tree.truncate_leaf() { + Ok(v) => v, + _ => { + return 0; + } // Error + }; + + unsafe { + slice::from_raw_parts_mut(rt_ret, 32).copy_from_slice( + &tree + .root_node() + .expect("Just generated without errors, root should be resolving") + .data() + .subtree_commitment, + ); + } + + truncate_len +} + +#[no_mangle] +pub extern "system" fn librustzcash_mmr_hash_node( + cbranch: u32, + n_ptr: *const [u8; zcash_mmr::MAX_NODE_DATA_SIZE], + h_ret: *mut u8, +) -> u32 { + let node_bytes: &[u8; zcash_mmr::MAX_NODE_DATA_SIZE] = unsafe { + match n_ptr.as_ref() { + Some(r) => r, + None => return 1, + } + }; + + let node = match MMRNodeData::from_bytes(cbranch, &node_bytes[..]) { + Ok(n) => n, + _ => return 1, // error + }; + + unsafe { + slice::from_raw_parts_mut(h_ret, 32).copy_from_slice(&node.hash()[..]); + } + + return 0; +} diff --git a/librustzcash/src/tests/mmr.rs b/librustzcash/src/tests/mmr.rs new file mode 100644 index 000000000..6e40f7f7a --- /dev/null +++ b/librustzcash/src/tests/mmr.rs @@ -0,0 +1,228 @@ +use zcash_mmr::{Entry, EntryLink, NodeData}; + +use crate::{librustzcash_mmr_append, librustzcash_mmr_delete}; + +const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat"); +const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat"); + +struct TreeView { + peaks: Vec<(u32, Entry)>, + extra: Vec<(u32, Entry)>, +} + +fn draft(into: &mut Vec<(u32, Entry)>, vec: &Vec, peak_pos: usize, h: u32) { + let node_data = vec[peak_pos - 1].clone(); + let peak: Entry = match h { + 0 => node_data.into(), + _ => Entry::new( + node_data, + EntryLink::Stored((peak_pos - (1 << h) - 1) as u32), + EntryLink::Stored((peak_pos - 2) as u32), + ), + }; + + into.push(((peak_pos - 1) as u32, peak)); +} + +fn prepare_tree(vec: &Vec) -> TreeView { + assert!(vec.len() > 0); + + // integer log2 of (vec.len()+1), -1 + let mut h = (32 - ((vec.len() + 1) as u32).leading_zeros() - 1) - 1; + let mut peak_pos = (1 << (h + 1)) - 1; + let mut nodes = Vec::new(); + + // used later + let mut last_peak_pos = 0; + let mut last_peak_h = 0; + + loop { + if peak_pos > vec.len() { + // left child, -2^h + peak_pos = peak_pos - (1 << h); + h = h - 1; + } + + if peak_pos <= vec.len() { + draft(&mut nodes, vec, peak_pos, h); + + // save to be used in next loop + last_peak_pos = peak_pos; + last_peak_h = h; + + // right sibling + peak_pos = peak_pos + (1 << (h + 1)) - 1; + } + + if h == 0 { + break; + } + } + + // for deletion, everything on the right slope of the last peak should be pre-loaded + let mut extra = Vec::new(); + let mut h = last_peak_h; + let mut peak_pos = last_peak_pos; + + while h > 0 { + let left_pos = peak_pos - (1 << h); + let right_pos = peak_pos - 1; + h = h - 1; + + // drafting left child + draft(&mut extra, vec, left_pos, h); + + // drafting right child + draft(&mut extra, vec, right_pos, h); + + // continuing on right slope + peak_pos = right_pos; + } + + TreeView { + peaks: nodes, + extra, + } +} + +fn preload_tree_append(vec: &Vec) -> (Vec, Vec<[u8; zcash_mmr::MAX_ENTRY_SIZE]>) { + assert!(vec.len() > 0); + + let tree_view = prepare_tree(vec); + + let mut indices = Vec::new(); + let mut bytes = Vec::new(); + + for (idx, entry) in tree_view.peaks.into_iter() { + let mut buf = [0u8; zcash_mmr::MAX_ENTRY_SIZE]; + entry + .write(&mut &mut buf[..]) + .expect("Cannot fail if enough buffer length"); + indices.push(idx); + bytes.push(buf); + } + + (indices, bytes) +} + +// also returns number of peaks +fn preload_tree_delete( + vec: &Vec, +) -> (Vec, Vec<[u8; zcash_mmr::MAX_ENTRY_SIZE]>, usize) { + assert!(vec.len() > 0); + + let tree_view = prepare_tree(vec); + + let mut indices = Vec::new(); + let mut bytes = Vec::new(); + + let peak_count = tree_view.peaks.len(); + + for (idx, entry) in tree_view + .peaks + .into_iter() + .chain(tree_view.extra.into_iter()) + { + let mut buf = [0u8; zcash_mmr::MAX_ENTRY_SIZE]; + entry + .write(&mut &mut buf[..]) + .expect("Cannot fail if enough buffer length"); + indices.push(idx); + bytes.push(buf); + } + + (indices, bytes, peak_count) +} + +fn load_nodes(bytes: &'static [u8]) -> Vec { + let mut res = Vec::new(); + let mut cursor = std::io::Cursor::new(bytes); + while (cursor.position() as usize) < bytes.len() { + let node_data = + zcash_mmr::NodeData::read(0, &mut cursor).expect("Statically checked to be correct"); + res.push(node_data); + } + + res +} + +#[test] +fn append() { + let nodes = load_nodes(NODE_DATA_16L); + let (indices, peaks) = preload_tree_append(&nodes); + + let mut rt_ret = [0u8; 32]; + + let mut buf_ret = Vec::<[u8; zcash_mmr::MAX_NODE_DATA_SIZE]>::with_capacity(32); + + let mut new_node_data = [0u8; zcash_mmr::MAX_NODE_DATA_SIZE]; + let new_node = NodeData { + consensus_branch_id: 0, + subtree_commitment: [0u8; 32], + start_time: 101, + end_time: 110, + start_target: 190, + end_target: 200, + start_sapling_root: [0u8; 32], + end_sapling_root: [0u8; 32], + subtree_total_work: Default::default(), + start_height: 10, + end_height: 10, + shielded_tx: 13, + }; + new_node + .write(&mut &mut new_node_data[..]) + .expect("Failed to write node data"); + + let result = librustzcash_mmr_append( + 0, + nodes.len() as u32, + indices.as_ptr(), + peaks.as_ptr(), + peaks.len(), + &new_node_data, + rt_ret.as_mut_ptr(), + buf_ret.as_mut_ptr(), + ); + + unsafe { + buf_ret.set_len(result as usize); + } + + assert_eq!(result, 2); + + let new_node_1 = + NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1"); + + let new_node_2 = + NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2"); + + assert_eq!(new_node_1.start_height, 10); + assert_eq!(new_node_1.end_height, 10); + + // this is combined new node (which is `new_node_1`) + the one which was there before (for block #9) + assert_eq!(new_node_2.start_height, 9); + assert_eq!(new_node_2.end_height, 10); + assert_eq!(new_node_2.shielded_tx, 27); +} + +#[test] +fn delete() { + let nodes = load_nodes(NODE_DATA_1023L); + let (indices, nodes, peak_count) = preload_tree_delete(&nodes); + + let mut rt_ret = [0u8; 32]; + + let result = librustzcash_mmr_delete( + 0, + nodes.len() as u32, + indices.as_ptr(), + nodes.as_ptr(), + peak_count, + indices.len() - peak_count, + rt_ret.as_mut_ptr(), + ); + + // Deleting from full tree of 9 height would result in cascade deleting of 10 nodes + assert_eq!(result, 10); +} diff --git a/librustzcash/src/tests/mod.rs b/librustzcash/src/tests/mod.rs index dba738767..bbaee0abb 100644 --- a/librustzcash/src/tests/mod.rs +++ b/librustzcash/src/tests/mod.rs @@ -4,6 +4,7 @@ use super::JUBJUB; mod key_agreement; mod key_components; +mod mmr; mod notes; mod signatures; diff --git a/librustzcash/src/tests/res/tree1023.dat b/librustzcash/src/tests/res/tree1023.dat new file mode 100644 index 0000000000000000000000000000000000000000..bfd79d10e851ff10e1ae67ff8dfcbcd6704e77f1 GIT binary patch literal 153867 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 IIZ28C0|YG$$p8QV literal 0 HcmV?d00001 diff --git a/librustzcash/src/tests/res/tree16.dat b/librustzcash/src/tests/res/tree16.dat new file mode 100644 index 0000000000000000000000000000000000000000..bde2e74d45daade78a0124c7336fd2283cd702f3 GIT binary patch literal 2352 zcmZQzzz?{A(jq{d2gDWll~P5Pk&%s%Iif%XazG4nP7@);RFG$4V&Am%Q!8us!f65< zTDRWWtzORjIQf~y6aQuV4nMuzZna(-6iOf`fkKHMPGV%@CFC)ApaKo3b9x9Vrh+^( zGY274G=U0?fEeVQX@nG0L7s($GjGAf8&h`dIqbMPB=~jJ4)tw9^)s(0E1!9n+L8Oi z{oy81D1n>=iZpsSiJ3*PUwQdaL&27_s|rdQLr?F^Gd%Ts_1(M2GCE@Bo}B2ep94xu zASZ#+5xAL#Rf#!UXGEPdU{Nm@voRExwo7Y$^ZK;e4^Sw9oCFFbdN_%d4O9~11@1uE03ZfA zXA54H)RAOo=OJWJAW%UR5QChvkC0+2$a8S;9%gYbmCAE`7_2DCeOY?q)76D1UP$U( zznoLyl5^MU)MHR6ft&;iC3-lCokQ~I)YI-Sc^~p!P2+Psf2Hxmmaw|Y6AF5>K1Y2% ze`#lVBPcC_oCHcs^l%a@hu$@j_$hO4UAlRwG+9jL%{AkVp?0@tO}ZL-{Mv8!WXUa} mpmGW1Bv83T4<|8l_yBFf3!;ItDL@Qz&I!CKsUykB$p-+L*^;aP literal 0 HcmV?d00001