mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-16 20:01:37 +00:00
trie/bintrie: test DeleteAccount produces a deterministic root
Two independent tries running the same UpdateAccount → DeleteAccount sequence must produce identical root hashes. Deletion does not return the trie to a pristine-empty root (zero blobs hash to non-zero leaves under the tombstone model), but the post-delete root must be deterministic across runs — any non-determinism in the tombstone-write path would silently fork the network on the first self-destruct after enabling flat state. Also pin the post-delete root != empty root invariant so a future change to the tombstone semantics surfaces here instead of in GetAccount's deletion-detection branch.
This commit is contained in:
parent
2a7effb6ca
commit
d38689e4ed
1 changed files with 42 additions and 0 deletions
|
|
@ -555,6 +555,48 @@ func TestDeleteAccountPreservesHeaderStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDeleteAccountHashIsDeterministic verifies that two independent tries
|
||||||
|
// running the same UpdateAccount → DeleteAccount sequence produce identical
|
||||||
|
// root hashes. This is the consensus-critical property of the tombstone
|
||||||
|
// model: deletion may not produce a pristine-empty root (zero blobs hash to
|
||||||
|
// non-zero leaves), but it MUST be deterministic across independent runs.
|
||||||
|
//
|
||||||
|
// A regression that introduced any non-determinism into the tombstone write
|
||||||
|
// path (e.g. relying on map iteration order, randomized offsets) would
|
||||||
|
// silently fork the network on the first self-destruct after enabling flat
|
||||||
|
// state.
|
||||||
|
func TestDeleteAccountHashIsDeterministic(t *testing.T) {
|
||||||
|
addr := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
||||||
|
codeHash := common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
|
||||||
|
acc := makeAccount(42, 1000, codeHash)
|
||||||
|
|
||||||
|
run := func() common.Hash {
|
||||||
|
tr := newTestTrie(t)
|
||||||
|
if err := tr.UpdateAccount(addr, acc, 0); err != nil {
|
||||||
|
t.Fatalf("UpdateAccount: %v", err)
|
||||||
|
}
|
||||||
|
if err := tr.DeleteAccount(addr); err != nil {
|
||||||
|
t.Fatalf("DeleteAccount: %v", err)
|
||||||
|
}
|
||||||
|
return tr.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
first := run()
|
||||||
|
second := run()
|
||||||
|
if first != second {
|
||||||
|
t.Fatalf("non-deterministic root after Update+Delete: first=%x second=%x", first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check the tombstone model itself: the post-delete root is NOT
|
||||||
|
// the empty-trie root, because zero blobs hash to non-zero leaves. If
|
||||||
|
// this assertion ever flips, the tombstone semantics have silently
|
||||||
|
// changed and the deletion-detection branch in GetAccount needs review.
|
||||||
|
empty := newTestTrie(t).Hash()
|
||||||
|
if first == empty {
|
||||||
|
t.Fatalf("post-delete root unexpectedly equals empty-trie root %x; tombstone semantics changed", empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBinaryTrieWitness(t *testing.T) {
|
func TestBinaryTrieWitness(t *testing.T) {
|
||||||
tracer := trie.NewPrevalueTracer()
|
tracer := trie.NewPrevalueTracer()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue