Add failing test for extending struct list
This commit is contained in:
parent
55ee8e20ae
commit
48cf75e394
@ -163,6 +163,11 @@ impl TreeHashCache {
|
|||||||
let byte_start = chunk_range.start * BYTES_PER_CHUNK;
|
let byte_start = chunk_range.start * BYTES_PER_CHUNK;
|
||||||
let byte_end = chunk_range.end * BYTES_PER_CHUNK;
|
let byte_end = chunk_range.end * BYTES_PER_CHUNK;
|
||||||
|
|
||||||
|
// Update the `chunk_modified` vec, marking all spliced-in nodes as changed.
|
||||||
|
self.chunk_modified.splice(
|
||||||
|
chunk_range.clone(),
|
||||||
|
vec![true; chunk_range.end - chunk_range.start],
|
||||||
|
);
|
||||||
self.cache.splice(byte_start..byte_end, replace_with)
|
self.cache.splice(byte_start..byte_end, replace_with)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,12 +127,28 @@ where
|
|||||||
}
|
}
|
||||||
ItemType::Composite | ItemType::List => {
|
ItemType::Composite | ItemType::List => {
|
||||||
let mut i = offset_handler.num_leaf_nodes;
|
let mut i = offset_handler.num_leaf_nodes;
|
||||||
for start_chunk in offset_handler.iter_leaf_nodes().rev() {
|
for &start_chunk in offset_handler.iter_leaf_nodes().rev() {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
match (other.get(i), self.get(i)) {
|
match (other.get(i), self.get(i)) {
|
||||||
// The item existed in the previous list and exsits in the current list.
|
// The item existed in the previous list and exsits in the current list.
|
||||||
(Some(old), Some(new)) => {
|
(Some(old), Some(new)) => {
|
||||||
new.cached_hash_tree_root(old, cache, *start_chunk)?;
|
new.cached_hash_tree_root(old, cache, start_chunk)?;
|
||||||
|
}
|
||||||
|
// The item existed in the previous list but does not exist in this list.
|
||||||
|
//
|
||||||
|
// I.e., the list has been shortened.
|
||||||
|
(Some(old), None) => {
|
||||||
|
// Splice out the entire tree of the removed node, replacing it with a
|
||||||
|
// single padding node.
|
||||||
|
let end_chunk = OffsetHandler::new(old, start_chunk)?.next_node();
|
||||||
|
cache.chunk_splice(start_chunk..end_chunk, vec![0; HASHSIZE]);
|
||||||
|
}
|
||||||
|
// The item existed in the previous list but does exist in this list.
|
||||||
|
//
|
||||||
|
// I.e., the list has been lengthened.
|
||||||
|
(None, Some(new)) => {
|
||||||
|
let bytes: Vec<u8> = TreeHashCache::new(new)?.into();
|
||||||
|
cache.chunk_splice(start_chunk..start_chunk + 1, bytes);
|
||||||
}
|
}
|
||||||
// The item didn't exist in the old list and doesn't exist in the new list,
|
// The item didn't exist in the old list and doesn't exist in the new list,
|
||||||
// nothing to do.
|
// nothing to do.
|
||||||
|
@ -408,6 +408,7 @@ fn extended_u64_vec_len_within_pow_2_boundary() {
|
|||||||
test_u64_vec_modifications(original_vec, modified_vec);
|
test_u64_vec_modifications(original_vec, modified_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
fn extended_u64_vec_len_outside_pow_2_boundary() {
|
fn extended_u64_vec_len_outside_pow_2_boundary() {
|
||||||
let original_vec: Vec<u64> = (0..2_u64.pow(5)).collect();
|
let original_vec: Vec<u64> = (0..2_u64.pow(5)).collect();
|
||||||
@ -416,6 +417,7 @@ fn extended_u64_vec_len_outside_pow_2_boundary() {
|
|||||||
|
|
||||||
test_u64_vec_modifications(original_vec, modified_vec);
|
test_u64_vec_modifications(original_vec, modified_vec);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn large_vec_of_u64_builds() {
|
fn large_vec_of_u64_builds() {
|
||||||
@ -437,9 +439,51 @@ fn large_vec_of_u64_builds() {
|
|||||||
assert_eq!(expected, cache);
|
assert_eq!(expected, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic test that covers:
|
||||||
|
///
|
||||||
|
/// 1. Produce a new cache from `original`.
|
||||||
|
/// 2. Do a differential hash between `original` and `modified`.
|
||||||
|
/// 3. Test that the cache generated matches the one we generate manually.
|
||||||
|
///
|
||||||
|
/// The `reference` vec is used to build the tree hash cache manually. `Inner` is just 4x `u64`, so
|
||||||
|
/// you can represent 2x `Inner` with a `reference` vec of len 8.
|
||||||
|
///
|
||||||
|
/// In effect it ensures that we can do a differential hash between two `Vec<Inner>`.
|
||||||
|
fn test_inner_vec_modifications(original: Vec<Inner>, modified: Vec<Inner>, reference: Vec<u64>) {
|
||||||
|
let mut cache = TreeHashCache::new(&original).unwrap();
|
||||||
|
|
||||||
|
modified
|
||||||
|
.cached_hash_tree_root(&original, &mut cache, 0)
|
||||||
|
.unwrap();
|
||||||
|
let modified_cache: Vec<u8> = cache.into();
|
||||||
|
|
||||||
|
// Build the reference vec.
|
||||||
|
|
||||||
|
let mut leaves = vec![];
|
||||||
|
let mut full_bytes = vec![];
|
||||||
|
|
||||||
|
for n in reference.chunks(4) {
|
||||||
|
let mut merkle = merkleize(join(vec![
|
||||||
|
int_to_bytes32(n[0]),
|
||||||
|
int_to_bytes32(n[1]),
|
||||||
|
int_to_bytes32(n[2]),
|
||||||
|
int_to_bytes32(n[3]),
|
||||||
|
]));
|
||||||
|
leaves.append(&mut merkle[0..HASHSIZE].to_vec());
|
||||||
|
full_bytes.append(&mut merkle);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut expected = merkleize(leaves);
|
||||||
|
expected.splice(3 * HASHSIZE.., full_bytes);
|
||||||
|
expected.append(&mut vec![0; HASHSIZE]);
|
||||||
|
|
||||||
|
// Compare the cached tree to the reference tree.
|
||||||
|
assert_trees_eq(&expected, &modified_cache);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn partial_modification_of_vec_of_inner() {
|
fn partial_modification_of_vec_of_inner() {
|
||||||
let original_vec = vec![
|
let original = vec![
|
||||||
Inner {
|
Inner {
|
||||||
a: 0,
|
a: 0,
|
||||||
b: 1,
|
b: 1,
|
||||||
@ -459,42 +503,87 @@ fn partial_modification_of_vec_of_inner() {
|
|||||||
d: 11,
|
d: 11,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let mut cache = TreeHashCache::new(&original_vec).unwrap();
|
|
||||||
|
|
||||||
let mut modified_vec = original_vec.clone();
|
let mut modified = original.clone();
|
||||||
modified_vec[1].a = 42;
|
modified[1].a = 42;
|
||||||
|
|
||||||
modified_vec
|
let mut reference_vec: Vec<u64> = (0..12).collect();
|
||||||
.cached_hash_tree_root(&original_vec, &mut cache, 0)
|
reference_vec[4] = 42;
|
||||||
.unwrap();
|
|
||||||
let modified_cache: Vec<u8> = cache.into();
|
|
||||||
|
|
||||||
// Build the reference vec.
|
test_inner_vec_modifications(original, modified, reference_vec);
|
||||||
|
|
||||||
let mut numbers: Vec<u64> = (0..12).collect();
|
|
||||||
numbers[4] = 42;
|
|
||||||
|
|
||||||
let mut leaves = vec![];
|
|
||||||
let mut full_bytes = vec![];
|
|
||||||
|
|
||||||
for n in numbers.chunks(4) {
|
|
||||||
let mut merkle = merkleize(join(vec![
|
|
||||||
int_to_bytes32(n[0]),
|
|
||||||
int_to_bytes32(n[1]),
|
|
||||||
int_to_bytes32(n[2]),
|
|
||||||
int_to_bytes32(n[3]),
|
|
||||||
]));
|
|
||||||
leaves.append(&mut merkle[0..HASHSIZE].to_vec());
|
|
||||||
full_bytes.append(&mut merkle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut expected = merkleize(leaves);
|
#[test]
|
||||||
expected.splice(3 * HASHSIZE.., full_bytes);
|
fn shortened_vec_of_inner_within_power_of_two_boundary() {
|
||||||
expected.append(&mut vec![0; HASHSIZE]);
|
let original = vec![
|
||||||
|
Inner {
|
||||||
|
a: 0,
|
||||||
|
b: 1,
|
||||||
|
c: 2,
|
||||||
|
d: 3,
|
||||||
|
},
|
||||||
|
Inner {
|
||||||
|
a: 4,
|
||||||
|
b: 5,
|
||||||
|
c: 6,
|
||||||
|
d: 7,
|
||||||
|
},
|
||||||
|
Inner {
|
||||||
|
a: 8,
|
||||||
|
b: 9,
|
||||||
|
c: 10,
|
||||||
|
d: 11,
|
||||||
|
},
|
||||||
|
Inner {
|
||||||
|
a: 12,
|
||||||
|
b: 13,
|
||||||
|
c: 14,
|
||||||
|
d: 15,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// Compare the cached tree to the reference tree.
|
let mut modified = original.clone();
|
||||||
|
modified.pop(); // remove the last element from the list.
|
||||||
|
|
||||||
assert_trees_eq(&expected, &modified_cache);
|
let reference_vec: Vec<u64> = (0..12).collect();
|
||||||
|
|
||||||
|
test_inner_vec_modifications(original, modified, reference_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lengthened_vec_of_inner_within_power_of_two_boundary() {
|
||||||
|
let original = vec![
|
||||||
|
Inner {
|
||||||
|
a: 0,
|
||||||
|
b: 1,
|
||||||
|
c: 2,
|
||||||
|
d: 3,
|
||||||
|
},
|
||||||
|
Inner {
|
||||||
|
a: 4,
|
||||||
|
b: 5,
|
||||||
|
c: 6,
|
||||||
|
d: 7,
|
||||||
|
},
|
||||||
|
Inner {
|
||||||
|
a: 8,
|
||||||
|
b: 9,
|
||||||
|
c: 10,
|
||||||
|
d: 11,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut modified = original.clone();
|
||||||
|
modified.push(Inner {
|
||||||
|
a: 12,
|
||||||
|
b: 13,
|
||||||
|
c: 14,
|
||||||
|
d: 15,
|
||||||
|
});
|
||||||
|
|
||||||
|
let reference_vec: Vec<u64> = (0..16).collect();
|
||||||
|
|
||||||
|
test_inner_vec_modifications(original, modified, reference_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user