From 2543bb836a9be3272fc364d60ff78bab57312991 Mon Sep 17 00:00:00 2001 From: Davidson Souza Date: Fri, 4 Nov 2022 14:29:00 -0300 Subject: [PATCH] Implementation of Proof::Update This method is useful for cached proofs, it can take a valid proof and update it with all of a block UTXOs, yielding a new (also valid) proof. It replaces all intermediate values in a proof with the new ones, also adds new positions that where't in the original one. Finally, calculates the new positions for each target. --- src/accumulator/proof.rs | 275 +++++++++++++++++++++- src/lib.rs | 1 + test_values/cached_proof_tests.json | 339 ++++++++++++++++++++++++++++ 3 files changed, 612 insertions(+), 3 deletions(-) create mode 100644 test_values/cached_proof_tests.json diff --git a/src/accumulator/proof.rs b/src/accumulator/proof.rs index c6953ea..7e6efa4 100644 --- a/src/accumulator/proof.rs +++ b/src/accumulator/proof.rs @@ -1,4 +1,5 @@ use super::stump::UpdateData; +use super::util::get_proof_positions; use crate::accumulator::{stump::Stump, types, util}; use bitcoin_hashes::{sha256, Hash}; @@ -249,15 +250,154 @@ impl Proof { pub fn update( self, cached_hashes: Vec, + add_hashes: Vec, block_targets: Vec, + remembers: Vec, update_data: UpdateData, ) -> Result<(Proof, Vec), String> { - self.update_proof_remove( + let (proof_after_deletion, cached_hashes) = self.update_proof_remove( block_targets, cached_hashes.clone(), update_data.new_del, update_data.prev_num_leaves, - ) + )?; + + let data_after_addition = proof_after_deletion.update_proof_add( + add_hashes, + cached_hashes.clone(), + remembers, + update_data.new_add, + update_data.prev_num_leaves, + update_data.to_destroy, + )?; + + Ok(data_after_addition) + } + fn update_proof_add( + self, + adds: Vec, + cached_del_hashes: Vec, + remembers: Vec, + new_nodes: Vec<(u64, sha256::Hash)>, + before_num_leaves: u64, + to_destroy: Vec, + ) -> Result<(Proof, Vec), String> { + // Combine the hashes with the targets. + let orig_targets_with_hash: Vec<(u64, sha256::Hash)> = self + .targets + .to_owned() + .into_iter() + .zip(cached_del_hashes.into_iter()) + .collect(); + + // Attach positions to the proof. + let proof_pos = get_proof_positions( + &self.targets, + before_num_leaves, + util::tree_rows(before_num_leaves), + ); + let proof_with_pos = proof_pos + .clone() + .into_iter() + .zip(self.hashes.clone()) + .collect(); + + // Remap the positions if we moved up a after the addition row. + let targets_after_remap = Proof::maybe_remap( + before_num_leaves, + adds.len() as u64, + orig_targets_with_hash.clone(), + ); + let mut final_targets = targets_after_remap.clone(); + let mut new_nodes_iter = new_nodes.iter(); + let mut proof_with_pos = + Proof::maybe_remap(before_num_leaves, adds.len() as u64, proof_with_pos); + + let num_leaves = before_num_leaves + (adds.len() as u64); + // Move up positions that need to be moved up due to the empty roots + // being written over. + for node in to_destroy { + final_targets = + Proof::calc_next_positions(&vec![node], &final_targets, num_leaves, true)?; + proof_with_pos = + Proof::calc_next_positions(&vec![node], &proof_with_pos, num_leaves, true)?; + } + + // remembers is an index telling what newly created UTXO should be cached + for remember in remembers { + let remember_target: Option<&sha256::Hash> = adds.get(remember as usize); + if let Some(remember_target) = remember_target { + let node = new_nodes_iter.find(|(_, hash)| *hash == *remember_target); + if let Some((pos, hash)) = node { + final_targets.push((*pos, *hash)); + } + } + } + + final_targets.sort(); + + let (new_target_pos, target_hashes) = final_targets.clone().into_iter().unzip(); + // Grab all the new nodes after this add. + let mut needed_proof_positions = + util::get_proof_positions(&new_target_pos, num_leaves, util::tree_rows(num_leaves)); + needed_proof_positions.sort(); + + // We'll use all elements from the old proof, as addition only creates new nodes + // in our proof (except for root destruction). But before using it, we have to + // compute the new positions, as adding new elements may move existing elements a few + // rows up. + let mut new_proof = proof_with_pos; + // Iterates over the needed positions and grab them from new_nodes + // All proof elements must come from the old proof or new_nodes. Old proof elements + // are already in new_proof. Some every missing element must be in new_nodes. + for pos in needed_proof_positions { + if let Some((_, hash)) = new_nodes.iter().find(|(node_pos, _)| pos == *node_pos) { + new_proof.push((pos, *hash)); + } else { + // This node must be in either new_nodes or in the old proof, otherwise we can't + // update our proof + if let None = new_proof.iter().find(|(proof_pos, _)| *proof_pos == pos) { + return Err(format!("Missing position {}", pos)); + } + } + } + new_proof.sort(); + + let (_, hashes): (Vec, Vec) = new_proof.into_iter().unzip(); + Ok(( + Proof { + hashes, + targets: new_target_pos, + }, + target_hashes, + )) + } + /// maybe_remap remaps the passed in hash and pos if the tree_rows increase after + /// adding the new nodes. + fn maybe_remap( + num_leaves: u64, + num_adds: u64, + positions: Vec<(u64, sha256::Hash)>, + ) -> Vec<(u64, sha256::Hash)> { + let new_forest_rows = util::tree_rows(num_leaves + num_adds); + let old_forest_rows = util::tree_rows(num_leaves); + let tree_rows = util::tree_rows(num_leaves); + let mut new_proofs = vec![]; + if new_forest_rows > old_forest_rows { + for (pos, hash) in positions.iter() { + let row = util::detect_row(*pos, tree_rows); + + let old_start_pos = util::start_position_at_row(row, old_forest_rows); + let new_start_pos = util::start_position_at_row(row, new_forest_rows); + + let offset = pos - old_start_pos; + let new_pos = offset + new_start_pos; + new_proofs.push((new_pos, *hash)); + } + return new_proofs; + } + + positions } /// update_proof_remove modifies the cached proof with the deletions that happen in the block proof. @@ -402,7 +542,7 @@ impl Proof { mod tests { use std::str::FromStr; - use bitcoin_hashes::{sha256, Hash, HashEngine}; + use bitcoin_hashes::{hex::FromHex, sha256, Hash, HashEngine}; use serde::Deserialize; use super::Proof; @@ -416,7 +556,136 @@ mod tests { proofhashes: Vec, expected: bool, } + /// This test checks whether our update proof works for different scenarios. We start + /// with a (valid) cached proof, then we receive `blocks`, like we would in normal Bitcoin + /// but for this test, block is just random data. For each block we update our Stump and + /// our proof as well, after that, our proof **must** still be valid for the latest Stump. + /// + /// Fix-me: Using derive for deserialize, when also using sha256::Hash leads to an odd + /// error that can't be easily fixed. Even bumping version doesn't appear to help. + /// Deriving hashes directly reduces the amount of boilerplate code used, and makes everything + /// more clearer, hence, it's preferable. + #[test] + fn test_update_proof() { + #[derive(Debug, Deserialize)] + struct JsonProof { + targets: Vec, + hashes: Vec, + } + #[derive(Debug, Deserialize)] + struct UpdatedData { + /// The newly created utxo to be added to our accumulator + adds: Vec, + /// The proof for all destroyed utxos + proof: JsonProof, + /// The hash of all destroyed utxos + del_hashes: Vec, + } + #[derive(Debug, Deserialize)] + struct TestData { + /// Blocks contains new utxos and utxos that are being deleted + update: UpdatedData, + /// The proof we have for our wallet's utxos + cached_proof: JsonProof, + /// A initial set of roots, may be empty for starting with an empty stump + initial_roots: Vec, + /// The number of leaves in the initial Stump + initial_leaves: u64, + /// The hash of all wallet's utxo + cached_hashes: Vec, + /// The indexes of all the new utxos to cache + remembers: Vec, + /// After we update our stump, which roots we expect? + expected_roots: Vec, + /// After we update the proof, the proof's target should be this + expected_targets: Vec, + /// After we update the proof, the cached hashes should be this + expected_cached_hashes: Vec, + } + let contents = std::fs::read_to_string("test_values/cached_proof_tests.json") + .expect("Something went wrong reading the file"); + + let values: Vec = + serde_json::from_str(contents.as_str()).expect("JSON deserialization error"); + for case_values in values { + let proof_hashes = case_values + .cached_proof + .hashes + .iter() + .map(|val| sha256::Hash::from_str(val).unwrap()) + .collect(); + let cached_hashes: Vec<_> = case_values + .cached_hashes + .iter() + .map(|val| sha256::Hash::from_str(val).unwrap()) + .collect(); + + let cached_proof = Proof::new(case_values.cached_proof.targets, proof_hashes); + let roots = case_values + .initial_roots + .into_iter() + .map(|hash| sha256::Hash::from_hex(&hash).unwrap()) + .collect(); + let stump = Stump { + leafs: case_values.initial_leaves, + roots, + }; + + let utxos = case_values + .update + .adds + .iter() + .map(|preimage| hash_from_u8(*preimage as u8)) + .collect(); + let del_hashes = case_values + .update + .del_hashes + .iter() + .map(|hash| sha256::Hash::from_hex(hash).unwrap()) + .collect(); + let block_proof_hashes: Vec<_> = case_values + .update + .proof + .hashes + .iter() + .map(|hash| sha256::Hash::from_hex(hash).unwrap()) + .collect(); + + let block_proof = + Proof::new(case_values.update.proof.targets.clone(), block_proof_hashes); + let (stump, updated) = stump.modify(&utxos, &del_hashes, &block_proof).unwrap(); + let remembers = case_values.cached_hashes.clone(); + let (cached_proof, cached_hashes) = cached_proof + .update( + cached_hashes.clone(), + utxos, + case_values.update.proof.targets, + case_values.remembers.clone(), + updated.clone(), + ) + .inspect_err(|_| println!("{:?}", remembers)) + .unwrap(); + + let res = cached_proof.verify(&cached_hashes, &stump); + + let expected_roots: Vec<_> = case_values + .expected_roots + .iter() + .map(|hash| sha256::Hash::from_hex(hash).unwrap()) + .collect(); + + let expected_cached_hashes: Vec<_> = case_values + .expected_cached_hashes + .iter() + .map(|hash| sha256::Hash::from_hex(hash).unwrap()) + .collect(); + assert_eq!(res, Ok(true)); + assert_eq!(cached_proof.targets, case_values.expected_targets); + assert_eq!(stump.roots, expected_roots); + assert_eq!(cached_hashes, expected_cached_hashes); + } + } #[test] fn test_calc_next_positions() { use super::Proof; diff --git a/src/lib.rs b/src/lib.rs index 2d5b405..3ed1b4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ +#![feature(result_option_inspect)] pub mod accumulator; diff --git a/test_values/cached_proof_tests.json b/test_values/cached_proof_tests.json new file mode 100644 index 0000000..297130e --- /dev/null +++ b/test_values/cached_proof_tests.json @@ -0,0 +1,339 @@ +[ + { + "update": { + "adds": [20, 21, 22], + "proof": { + "targets": [0, 1], + "hashes": [ + "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b", + "29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7" + ] + }, + "del_hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a" + ] + }, + "remembers": [], + "cached_proof": { + "targets": [4, 6, 7], + "hashes": [ + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386" + ] + }, + "initial_stump": "Add 8 leaves [0, 1, 2, 3, 4, 5, 6, 7]", + "initial_roots": [ + "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42" + ], + "initial_leaves": 8, + "cached_hashes": [ + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ], + "expected_roots": [ + "97491b30a42410dc3267d17933cf5e1b55cfb92ebab2dcf1bcd098032dacee95", + "0d451c2d9366705a3557fd038c25a40a348cc0effa2dd00dfaaba65749cd0915", + "7cb7c4547cf2653590d7a9ace60cc623d25148adfbc88a89aeb0ef88da7839ba" + ], + "expected_targets": [4, 6, 7], + "expected_cached_hashes": [ + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ] + }, + { + "update": { + "adds": [8], + "proof": { + "targets": [0, 1, 4], + "hashes": [ + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b", + "34028bbc87000c39476cdc60cf80ca32d579b3a0e2d3f80e0ad8c3739a01aa91" + ] + }, + "del_hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71" + ] + }, + "remembers": [0], + "cached_proof": { + "targets": [4, 6, 7], + "hashes": [ + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386" + ] + }, + "initial_stump": "Add 8 leaves [0, 1, 2, 3, 4, 5, 6, 7]", + "initial_roots": [ + "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42" + ], + "initial_leaves": 8, + "cached_hashes": [ + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ], + "expected_roots": [ + "7f6df6757821144278f5d535261ab4ffc55e2618f7bc62f1a19e7d289061b08b", + "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a" + ], + "expected_targets": [6, 7, 8], + "expected_cached_hashes": [ + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a" + ] + }, + { + "update": { + "adds": [8], + "proof": { + "targets": [4], + "hashes": [ + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "34028bbc87000c39476cdc60cf80ca32d579b3a0e2d3f80e0ad8c3739a01aa91", + "df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386" + ] + }, + "del_hashes": [ + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71" + ] + }, + "remembers": [0], + "cached_proof": { + "targets": [4, 5, 7], + "hashes": [ + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386" + ] + }, + "initial_stump": "Add 8 leaves [0, 1, 2, 3, 4, 5, 6, 7]", + "initial_roots": [ + "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42" + ], + "initial_leaves": 8, + "cached_hashes": [ + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ], + "expected_roots": [ + "f1e8f77fb6c03d3e62acc1824e6671da21bb643f362a9af5171167bfdfbbb9cd", + "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a" + ], + "expected_targets": [7, 8, 18], + "expected_cached_hashes": [ + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db" + ] + }, + { + "update": { + "adds": [], + "proof": { + "targets": [4, 5, 6, 7], + "hashes": [ + "df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386" + ] + }, + "del_hashes": [ + "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ] + }, + "remembers": [], + "cached_proof": { + "targets": [1, 8, 12], + "hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9", + "9d1e0e2d9459d06523ad13e28a4093c2316baafe7aec5b25f30eba2e113599c4", + "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b", + "c413035120e8c9b0ca3e40c93d06fe60a0d056866138300bb1f1dd172b4923c3", + "29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7" + ] + }, + "initial_stump": "Add 14 leaves [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]", + "initial_roots": [ + "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42", + "9c053db406c1a077112189469a3aca0573d3481bef09fa3d2eda3304d7d44be8", + "55d0a0ef8f5c25a9da266b36c0c5f4b31008ece82df2512c8966bddcc27a66a0" + ], + "initial_leaves": 14, + "cached_hashes": [ + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a", + "ef6cbd2161eaea7943ce8693b9824d23d1793ffb1c0fca05b600d3899b44c977" + ], + "expected_roots": [ + "df46b17be5f66f0750a4b3efa26d4679db170a72d41eb56c3e4ff75a58c65386", + "9c053db406c1a077112189469a3aca0573d3481bef09fa3d2eda3304d7d44be8", + "55d0a0ef8f5c25a9da266b36c0c5f4b31008ece82df2512c8966bddcc27a66a0" + ], + "expected_targets": [8, 12, 17], + "expected_cached_hashes": [ + "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a", + "ef6cbd2161eaea7943ce8693b9824d23d1793ffb1c0fca05b600d3899b44c977", + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a" + ] + }, + { + "update": { + "adds": [], + "proof": { + "targets": [0], + "hashes": [ + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b", + "29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7" + ] + }, + "del_hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d" + ] + }, + "remembers": [], + "cached_proof": { + "targets": [0, 7], + "hashes": [ + "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b", + "9eec588c41d87b16b0ee226cb38da3864f9537632321d8be855a73d5616dcc73" + ] + }, + "initial_stump": "Add 8 leaves [0, 1, 2, 3, 4, 5, 6, 7]", + "initial_roots": [ + "b151a956139bb821d4effa34ea95c17560e0135d1e4661fc23cedc3af49dac42" + ], + "initial_leaves": 8, + "cached_hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ], + "expected_roots": [ + "726fdd3b432cc59e68487d126e70f0db74a236267f8daeae30b31839a4e7ebed" + ], + "expected_targets": [7], + "expected_cached_hashes": [ + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879" + ] + }, + + { + "update": { + "adds": [], + "proof": { + "targets": [3, 6], + "hashes": [ + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "7a5db95c85dd117a20ca1b5b2ef50f61ee9529f40a957d97757c3a5b5ca7f5dd", + "2c358bbc9182d5eaf8ae15c50fbe0dcd45fc794bdc73eafffddedbb0f6bab327" + ] + }, + "del_hashes": [ + "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6" + ] + }, + "remembers": [], + "cached_proof": { + "targets": [2, 3, 7, 32, 34, 50, 52], + "hashes": [ + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6", + "e799acb98a071c4884707e4bc8c093ba22571c8d84cc0223ab0c2c9327313a5b", + "ff4b5145903ab6a21824078a0f2bce2fb27739e9e80aa8195f2d3be70b12fc79", + "d1366f2a8c5a8fe1fc9b581a759ab6333e8d1683c74e09904d5acb50a9bffd5b" + ] + }, + "initial_stump": "Add 32 leaves [0, 1, 2, 3, 4, ... 29, 30, 31], then delete [1, 4, 8, 9, 10, 16, 17, 18]", + "initial_roots": [ + "0f434fd19b46d19ad44dd83b205c8e8c00f1b6d5701427f2c0524304dcbdf1d8" + ], + "initial_leaves": 32, + "cached_hashes": [ + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6", + "ab897fbdedfa502b2d839b6a56100887dccdc507555c282e59589e06300a62e2" + ], + "expected_roots": [ + "bbe46ee06d681bb5bad60c1b4860566e5e78b489b227cf927ecfbde599163dda" + ], + "expected_targets": [32, 33, 34, 35, 50, 52], + "expected_cached_hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6", + "ab897fbdedfa502b2d839b6a56100887dccdc507555c282e59589e06300a62e2" + ] + }, + { + "update": { + "adds": [], + "proof": { + "targets": [3, 6], + "hashes": [ + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db" + ] + }, + "del_hashes": [ + "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5", + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6" + ] + }, + "remembers": [], + "cached_proof": { + "targets": [2, 3, 7, 16, 18, 26], + "hashes": [ + "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6" + ] + }, + "initial_stump": "Add 12 leaves [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], then delete [1, 4, 8, 9, 10]", + "initial_roots": [ + "5093a5ae2bceeef66d9af3f4f1ed71acffe4932cd02afca5d2dc659dca5c418f", + "e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6" + ], + "initial_leaves": 12, + "cached_hashes": [ + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6" + ], + "expected_roots": [ + "2bfc2e9199263e7b2ef762afeb2fede84eb4fac37f3f28fd22d5761c8390b875", + "e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6" + ], + "expected_targets": [16, 17, 18, 19, 26], + "expected_cached_hashes": [ + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", + "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986", + "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db", + "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879", + "e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6" + ] + } +] \ No newline at end of file