Skip to content

Commit e3e202b

Browse files
committed
Handle edge case of extension with modified prefix
1 parent fb2aa7d commit e3e202b

File tree

1 file changed

+59
-25
lines changed

1 file changed

+59
-25
lines changed

src/storage/overlay_root.rs

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -180,17 +180,39 @@ impl StorageEngine {
180180
stats,
181181
updated_storage_branch_nodes,
182182
)?;
183-
let hash = node_hash.unwrap();
184-
println!("Adding unaffected branch: {:?}", path_prefix);
185-
hash_builder.add_branch(path_prefix.clone(), hash, true);
186-
stats.account.branch_nodes += 1;
187-
self.process_nonoverlapping_overlay_state(
188-
context,
189-
&post_overlay,
190-
hash_builder,
191-
stats,
192-
updated_storage_branch_nodes,
193-
)?;
183+
if !node.prefix().is_empty() {
184+
// The node is actually an extension + branch node. We cannot add it by hash
185+
// as the extension prefix may have changed, which
186+
// would in turn change the hash. We need to
187+
// traverse the node via its full path.
188+
println!(
189+
"Processing extension + branch: {:?} / {:?}",
190+
path_prefix, full_path
191+
);
192+
self.handle_affected_node_with_overlay(
193+
context,
194+
&with_prefix,
195+
hash_builder,
196+
slotted_page,
197+
&node,
198+
&full_path,
199+
stats,
200+
updated_storage_branch_nodes,
201+
)?;
202+
} else {
203+
// This is a true branch node, so we can add it by hash.
204+
let hash = node_hash.unwrap();
205+
println!("Adding unaffected branch: {:?}", path_prefix);
206+
hash_builder.add_branch(path_prefix.clone(), hash, true);
207+
stats.account.branch_nodes += 1;
208+
self.process_nonoverlapping_overlay_state(
209+
context,
210+
&post_overlay,
211+
hash_builder,
212+
stats,
213+
updated_storage_branch_nodes,
214+
)?;
215+
}
194216
return Ok(());
195217
} else {
196218
let (pre_overlay, with_prefix, post_overlay) =
@@ -309,6 +331,7 @@ impl StorageEngine {
309331
}
310332
None => {
311333
// Tombstone - skip
334+
println!("Skipping tombstone: {:?}", path);
312335
last_processed_path = Some(path);
313336
}
314337
}
@@ -355,11 +378,18 @@ impl StorageEngine {
355378
)?;
356379
}
357380
NodeKind::StorageLeaf { .. } => {
358-
if let Some((_, Some(OverlayValue::Storage(value)))) = overlay.get(0) {
359-
hash_builder.add_leaf(full_path.clone(), &self.encode_storage(value)?);
360-
stats.storage.overlay_leaves += 1;
361-
} else {
362-
panic!("Storage leaf does not have a valid overlay: {full_path:?}");
381+
match overlay.get(0) {
382+
Some((_, Some(OverlayValue::Storage(value)))) => {
383+
hash_builder.add_leaf(full_path.clone(), &self.encode_storage(value)?);
384+
stats.storage.overlay_leaves += 1;
385+
}
386+
Some((_, None)) => {
387+
println!("Skipping deleted storage leaf: {:?}", full_path);
388+
stats.storage.overlay_leaves += 1;
389+
}
390+
_ => {
391+
panic!("Storage leaf does not have a valid overlay: {full_path:?}");
392+
}
363393
}
364394
}
365395
}
@@ -396,22 +426,28 @@ impl StorageEngine {
396426
children[i as usize] = (child_path, child_overlay, node.child(i as u8).unwrap());
397427
}
398428

399-
let num_children = children
429+
// This conservatively counts the minimum number of children that will exist. It may
430+
// undercount.
431+
let min_num_children = children
400432
.iter()
401433
.filter(|(_, child_overlay, child_pointer)| {
402434
if let Some((_, value)) = child_overlay.get(0) {
435+
// If the overlayed value is none, this branch path might be removed.
403436
return value.is_some();
404437
} else if child_pointer.is_some() {
438+
// If there is no overlayed value and an existing child, then this path will
439+
// definitely exist.
405440
return true;
406441
}
407442

408443
false
409444
})
410445
.count();
411446

412-
if num_children > 1 {
413-
// We have at least 2 children, so we can add non-overlayed children by hash instead of
414-
// traversing them. This can save many lookups, hashes, and page reads.
447+
if min_num_children > 1 {
448+
// We are guaranteed to have at least 2 children, keeping this branch node alive.
449+
// We can add non-overlayed children by hash instead of traversing them. This can save
450+
// many lookups, hashes, and page reads.
415451
for (child_path, child_overlay, child_pointer) in children.iter() {
416452
if let Some((path, _)) = child_overlay.get(0) {
417453
if &path == child_path {
@@ -476,8 +512,8 @@ impl StorageEngine {
476512
return Ok(());
477513
}
478514

479-
// Otherwise, this branch may no longer exist, so we need to traverse and add the children
480-
// directly
515+
// Otherwise, it is possible that this branch might be removed.
516+
// We need to traverse and add the children directly in order to be safe.
481517
for (child_path, child_overlay, child_pointer) in children.iter() {
482518
if let Some((path, _)) = child_overlay.get(0) {
483519
if &path == child_path {
@@ -573,7 +609,7 @@ impl StorageEngine {
573609
}
574610
} else {
575611
// If overlay_value is None, it's a deletion - skip
576-
// println!("Skipping deletion: {full_path:?}");
612+
println!("Skipping deleted account: {full_path:?}");
577613
None
578614
}
579615
} else {
@@ -2002,8 +2038,6 @@ mod tests {
20022038
db.storage_engine.compute_root_with_overlay(&context, &overlay).unwrap();
20032039
assert_ne!(overlay_root, initial_root);
20042040

2005-
// println!("Overlay branches: {:?}", overlay_branches);
2006-
20072041
for (path, branch) in overlay_branches.iter() {
20082042
if let Some(root_hash) = branch.root_hash {
20092043
overlay_mut_with_branches.insert(path.clone(), Some(OverlayValue::Hash(root_hash)));

0 commit comments

Comments
 (0)