core/state: added journal fuzzer

This commit is contained in:
Marius van der Wijden 2024-11-21 11:08:00 +01:00 committed by Martin Holst Swende
parent d4072dec1a
commit 08b5bb9182
No known key found for this signature in database
GPG Key ID: 683B438C05A5DDF0
1 changed files with 173 additions and 0 deletions

View File

@ -18,6 +18,8 @@
package state package state
import ( import (
"fmt"
"math/rand/v2"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -132,3 +134,174 @@ func testJournalRefunds(t *testing.T, j journal) {
t.Fatalf("have %d want %d", have, want) t.Fatalf("have %d want %d", have, want)
} }
} }
func FuzzJournals(f *testing.F) {
randByte := func() byte {
return byte(rand.Int())
}
randBool := func() bool {
return rand.Int()%2 == 0
}
randAccount := func() *types.StateAccount {
return &types.StateAccount{
Nonce: uint64(randByte()),
Balance: uint256.NewInt(uint64(randByte())),
Root: types.EmptyRootHash,
CodeHash: types.EmptyCodeHash[:],
}
}
f.Fuzz(func(t *testing.T, operations []byte) {
var (
statedb1, _ = New(types.EmptyRootHash, NewDatabaseForTesting())
statedb2, _ = New(types.EmptyRootHash, NewDatabaseForTesting())
linear = newLinearJournal()
sparse = newSparseJournal()
)
statedb1.journal = linear
statedb2.journal = sparse
linear.snapshot()
sparse.snapshot()
for _, o := range operations {
switch o {
case 0:
addr := randByte()
linear.accessListAddAccount(common.Address{addr})
sparse.accessListAddAccount(common.Address{addr})
statedb1.accessList.AddAddress(common.Address{addr})
statedb2.accessList.AddAddress(common.Address{addr})
case 1:
addr := randByte()
slot := randByte()
linear.accessListAddSlot(common.Address{addr}, common.Hash{slot})
sparse.accessListAddSlot(common.Address{addr}, common.Hash{slot})
statedb1.accessList.AddSlot(common.Address{addr}, common.Hash{slot})
statedb2.accessList.AddSlot(common.Address{addr}, common.Hash{slot})
case 2:
addr := randByte()
account := randAccount()
destructed := randBool()
newContract := randBool()
linear.balanceChange(common.Address{addr}, account, destructed, newContract)
sparse.balanceChange(common.Address{addr}, account, destructed, newContract)
case 3:
linear = linear.copy().(*linearJournal)
sparse = sparse.copy().(*sparseJournal)
case 4:
addr := randByte()
account := randAccount()
linear.createContract(common.Address{addr}, account)
sparse.createContract(common.Address{addr}, account)
case 5:
addr := randByte()
linear.createObject(common.Address{addr})
sparse.createObject(common.Address{addr})
case 6:
addr := randByte()
account := randAccount()
linear.destruct(common.Address{addr}, account)
sparse.destruct(common.Address{addr}, account)
case 7:
txHash := randByte()
linear.logChange(common.Hash{txHash})
sparse.logChange(common.Hash{txHash})
case 8:
addr := randByte()
account := randAccount()
destructed := randBool()
newContract := randBool()
linear.nonceChange(common.Address{addr}, account, destructed, newContract)
sparse.nonceChange(common.Address{addr}, account, destructed, newContract)
case 9:
refund := randByte()
linear.refundChange(uint64(refund))
sparse.refundChange(uint64(refund))
case 10:
addr := randByte()
account := randAccount()
linear.setCode(common.Address{addr}, account)
sparse.setCode(common.Address{addr}, account)
case 11:
addr := randByte()
key := randByte()
prev := randByte()
origin := randByte()
linear.storageChange(common.Address{addr}, common.Hash{key}, common.Hash{prev}, common.Hash{origin})
sparse.storageChange(common.Address{addr}, common.Hash{key}, common.Hash{prev}, common.Hash{origin})
case 12:
addr := randByte()
account := randAccount()
destructed := randBool()
newContract := randBool()
linear.touchChange(common.Address{addr}, account, destructed, newContract)
sparse.touchChange(common.Address{addr}, account, destructed, newContract)
case 13:
addr := randByte()
key := randByte()
prev := randByte()
linear.transientStateChange(common.Address{addr}, common.Hash{key}, common.Hash{prev})
sparse.transientStateChange(common.Address{addr}, common.Hash{key}, common.Hash{prev})
case 14:
linear.reset()
sparse.reset()
case 15:
linear.snapshot()
sparse.snapshot()
case 16:
linear.discardSnapshot()
sparse.discardSnapshot()
case 17:
linear.revertSnapshot(statedb1)
sparse.revertSnapshot(statedb2)
case 18:
accs1 := linear.dirtyAccounts()
accs2 := linear.dirtyAccounts()
if len(accs1) != len(accs2) {
panic(fmt.Sprintf("mismatched accounts: %v %v", accs1, accs2))
}
for _, val := range accs1 {
found := false
for _, val2 := range accs2 {
if val == val2 {
if found {
panic(fmt.Sprintf("account found twice: %v %v account %v", accs1, accs2, val))
}
found = true
}
}
if !found {
panic(fmt.Sprintf("missing account: %v %v account %v", accs1, accs2, val))
}
}
}
}
// After all operations have been processed, verify equality
accs1 := linear.dirtyAccounts()
accs2 := linear.dirtyAccounts()
for _, val := range accs1 {
found := false
for _, val2 := range accs2 {
if val == val2 {
if found {
panic(fmt.Sprintf("account found twice: %v %v account %v", accs1, accs2, val))
}
found = true
}
}
if !found {
panic(fmt.Sprintf("missing account: %v %v account %v", accs1, accs2, val))
}
}
h1, err1 := statedb1.Commit(0, false)
h2, err2 := statedb2.Commit(0, false)
if err1 != err2 {
panic(fmt.Sprintf("mismatched errors: %v %v", err1, err2))
}
if h1 != h2 {
panic(fmt.Sprintf("mismatched roots: %v %v", h1, h2))
}
})
}