core/state: better randomized testing (postcheck) on journalling

This commit is contained in:
Martin Holst Swende 2024-04-23 13:34:51 +02:00
parent 4f4f9d88d3
commit 903bf512a9
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
3 changed files with 93 additions and 4 deletions

View file

@ -17,7 +17,10 @@
package state
import (
"fmt"
"maps"
"slices"
"strings"
"github.com/ethereum/go-ethereum/common"
)
@ -130,3 +133,34 @@ func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
func (al *accessList) DeleteAddress(address common.Address) {
delete(al.addresses, address)
}
// Equal returns true if the two access lists are identical
func (al *accessList) Equal(other *accessList) bool {
if !maps.Equal(al.addresses, other.addresses) {
return false
}
return slices.EqualFunc(al.slots, other.slots,
func(m map[common.Hash]struct{}, m2 map[common.Hash]struct{}) bool {
return maps.Equal(m, m2)
})
}
func (al *accessList) PrettyPrint() string {
out := new(strings.Builder)
var sortedAddrs []common.Address
for addr, _ := range al.addresses {
sortedAddrs = append(sortedAddrs, addr)
}
slices.SortFunc(sortedAddrs, common.Address.Cmp)
for _, addr := range sortedAddrs {
idx := al.addresses[addr]
fmt.Fprintf(out, "%#x : (idx %d)\n", addr, idx)
if idx >= 0 {
slotmap := al.slots[idx]
for h, _ := range slotmap {
fmt.Fprintf(out, " %#x\n", h)
}
}
}
return out.String()
}

View file

@ -21,6 +21,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"maps"
"math"
"math/rand"
"reflect"
@ -609,11 +610,29 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
})
}
// Check transient storage.
{
have := state.transientStorage
want := checkstate.transientStorage
eq := maps.EqualFunc(have, want,
func(a Storage, b Storage) bool {
return maps.Equal(a, b)
})
if !eq {
return fmt.Errorf("transient storage differs ,have\n%v\nwant\n%v",
have.PrettyPrint(),
want.PrettyPrint())
}
}
if err != nil {
return err
}
}
if !checkstate.accessList.Equal(state.accessList) { // Check access lists
return fmt.Errorf("AccessLists are wrong, have \n%v\nwant\n%v",
checkstate.accessList.PrettyPrint(),
state.accessList.PrettyPrint())
}
if state.GetRefund() != checkstate.GetRefund() {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
state.GetRefund(), checkstate.GetRefund())

View file

@ -17,6 +17,10 @@
package state
import (
"fmt"
"slices"
"strings"
"github.com/ethereum/go-ethereum/common"
)
@ -30,11 +34,20 @@ func newTransientStorage() transientStorage {
// Set sets the transient-storage `value` for `key` at the given `addr`.
func (t transientStorage) Set(addr common.Address, key, value common.Hash) {
if value == (common.Hash{}) { // this is a 'delete'
if _, ok := t[addr]; ok {
delete(t[addr], key)
if len(t[addr]) == 0 {
delete(t, addr)
}
}
} else {
if _, ok := t[addr]; !ok {
t[addr] = make(Storage)
}
t[addr][key] = value
}
}
// Get gets the transient storage for `key` at the given `addr`.
func (t transientStorage) Get(addr common.Address, key common.Hash) common.Hash {
@ -53,3 +66,26 @@ func (t transientStorage) Copy() transientStorage {
}
return storage
}
func (t transientStorage) PrettyPrint() string {
out := new(strings.Builder)
var sortedAddrs []common.Address
for addr := range t {
sortedAddrs = append(sortedAddrs, addr)
slices.SortFunc(sortedAddrs, common.Address.Cmp)
}
for _, addr := range sortedAddrs {
fmt.Fprintf(out, "%#x:", addr)
var sortedKeys []common.Hash
storage := t[addr]
for key := range storage {
sortedKeys = append(sortedKeys, key)
}
slices.SortFunc(sortedKeys, common.Hash.Cmp)
for _, key := range sortedKeys {
fmt.Fprintf(out, " %X : %X\n", key, storage[key])
}
}
return out.String()
}