core/types/bal: remove ssz testdata and add tests

This commit is contained in:
Gary Rong 2025-07-11 16:10:02 +08:00
parent 0b3283a030
commit 1dc017c289
53 changed files with 179 additions and 99 deletions

View file

@ -76,7 +76,7 @@ type ConstructionBlockAccessList struct {
Accounts map[common.Address]*ConstructionAccountAccess Accounts map[common.Address]*ConstructionAccountAccess
} }
// NewBlockAccessList instantiates an empty access list. // NewConstructionBlockAccessList instantiates an empty access list.
func NewConstructionBlockAccessList() ConstructionBlockAccessList { func NewConstructionBlockAccessList() ConstructionBlockAccessList {
return ConstructionBlockAccessList{ return ConstructionBlockAccessList{
Accounts: make(map[common.Address]*ConstructionAccountAccess), Accounts: make(map[common.Address]*ConstructionAccountAccess),

View file

@ -19,16 +19,16 @@ package bal
import ( import (
"bytes" "bytes"
"cmp" "cmp"
"errors"
"fmt" "fmt"
"io" "io"
"maps" "maps"
"slices" "slices"
"strings" "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -47,19 +47,15 @@ type BlockAccessList struct {
// according to the spec or any code changes are contained which exceed protocol // according to the spec or any code changes are contained which exceed protocol
// max code size. // max code size.
func (e *BlockAccessList) Validate() error { func (e *BlockAccessList) Validate() error {
var ( if !slices.IsSortedFunc(e.Accesses, func(a, b AccountAccess) int {
prev *[20]byte return bytes.Compare(a.Address[:], b.Address[:])
) }) {
return errors.New("block access list accounts not in lexicographic order")
}
for _, entry := range e.Accesses { for _, entry := range e.Accesses {
if prev != nil {
if bytes.Compare(entry.Address[:], (*prev)[:]) <= 0 {
return fmt.Errorf("block access list accounts not in lexicographic order")
}
}
if err := entry.validate(); err != nil { if err := entry.validate(); err != nil {
return err return err
} }
prev = &entry.Address
} }
return nil return nil
} }
@ -115,16 +111,12 @@ type encodingSlotWrites struct {
// validate returns an instance of the encoding-representation slot writes in // validate returns an instance of the encoding-representation slot writes in
// working representation. // working representation.
func (e *encodingSlotWrites) validate() error { func (e *encodingSlotWrites) validate() error {
var prev *uint16 if slices.IsSortedFunc(e.Accesses, func(a, b encodingStorageWrite) int {
for _, write := range e.Accesses { return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
if prev != nil { }) {
if *prev >= write.TxIdx { return nil
return fmt.Errorf("storage write tx indices not in order")
}
}
prev = &write.TxIdx
} }
return nil return errors.New("storage write tx indices not in order")
} }
// AccountAccess is the encoding format of ConstructionAccountAccess. // AccountAccess is the encoding format of ConstructionAccountAccess.
@ -141,52 +133,37 @@ type AccountAccess struct {
// If any of the keys in the encoding object are not ordered according to the // If any of the keys in the encoding object are not ordered according to the
// spec, an error is returned. // spec, an error is returned.
func (e *AccountAccess) validate() error { func (e *AccountAccess) validate() error {
// Convert slot writes // Check the storage write slots are sorted in order
var prevSlotWrite *[32]byte if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int {
return bytes.Compare(a.Slot[:], b.Slot[:])
}) {
return errors.New("storage writes slots not in lexicographic order")
}
for _, write := range e.StorageWrites { for _, write := range e.StorageWrites {
if prevSlotWrite != nil {
if bytes.Compare((*prevSlotWrite)[:], write.Slot[:]) >= 0 {
return fmt.Errorf("storage writes slots not in lexicographic order")
}
}
prevSlotWrite = &write.Slot
if err := write.validate(); err != nil { if err := write.validate(); err != nil {
return err return err
} }
} }
// Convert slot reads // Check the storage read slots are sorted in order
var prevSlotRead *[32]byte if !slices.IsSortedFunc(e.StorageReads, func(a, b [32]byte) int {
for _, read := range e.StorageReads { return bytes.Compare(a[:], b[:])
if prevSlotRead != nil { }) {
if bytes.Compare((*prevSlotRead)[:], read[:]) >= 0 { return errors.New("storage read slots not in lexicographic order")
return fmt.Errorf("storage read slots not in lexicographic order")
}
}
prevSlotRead = &read
} }
// Convert balance changes // Check the balance changes are sorted in order
var prevBalanceIndex *uint16 if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int {
for _, balanceChange := range e.BalanceChanges { return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
if prevBalanceIndex != nil { }) {
if *prevBalanceIndex >= balanceChange.TxIdx { return errors.New("balance changes not in ascending order by tx index")
return fmt.Errorf("balance changes not in ascending order by tx index")
}
}
prevBalanceIndex = &balanceChange.TxIdx
} }
// Convert nonce changes // Check the nonce changes are sorted in order
var prevNonceIndex *uint16 if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int {
for _, nonceChange := range e.NonceChanges { return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
if prevNonceIndex != nil { }) {
if *prevNonceIndex >= nonceChange.TxIdx { return errors.New("nonce changes not in ascending order by tx index")
return fmt.Errorf("nonce diffs not in ascending order by tx index")
}
}
prevNonceIndex = &nonceChange.TxIdx
} }
// Convert code change // Convert code change
@ -201,36 +178,15 @@ func (e *AccountAccess) validate() error {
// Copy returns a deep copy of the account access // Copy returns a deep copy of the account access
func (e *AccountAccess) Copy() AccountAccess { func (e *AccountAccess) Copy() AccountAccess {
res := AccountAccess{ res := AccountAccess{
Address: e.Address, Address: e.Address,
StorageReads: slices.Clone(e.StorageReads),
BalanceChanges: slices.Clone(e.BalanceChanges),
NonceChanges: slices.Clone(e.NonceChanges),
} }
for _, storageWrite := range e.StorageWrites { for _, storageWrite := range e.StorageWrites {
cpy := encodingSlotWrites{ res.StorageWrites = append(res.StorageWrites, encodingSlotWrites{
Slot: storageWrite.Slot, Slot: storageWrite.Slot,
Accesses: slices.Clone(storageWrite.Accesses), Accesses: slices.Clone(storageWrite.Accesses),
}
for _, slotWrite := range storageWrite.Accesses {
cpy.Accesses = append(cpy.Accesses, encodingStorageWrite{
TxIdx: slotWrite.TxIdx,
ValueAfter: slotWrite.ValueAfter,
})
}
res.StorageWrites = append(res.StorageWrites, cpy)
}
res.StorageReads = slices.Clone(e.StorageReads)
for _, balanceChange := range e.BalanceChanges {
res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{
TxIdx: balanceChange.TxIdx,
Balance: balanceChange.Balance,
})
}
for _, nonceChange := range e.NonceChanges {
res.NonceChanges = append(res.NonceChanges, encodingAccountNonce{
TxIdx: nonceChange.TxIdx,
Nonce: nonceChange.Nonce,
}) })
} }
if len(e.Code) == 1 { if len(e.Code) == 1 {

View file

@ -18,10 +18,13 @@ package bal
import ( import (
"bytes" "bytes"
"cmp"
"reflect" "reflect"
"slices"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -33,7 +36,7 @@ func equalBALs(a *BlockAccessList, b *BlockAccessList) bool {
return true return true
} }
func makeTestBAL() *ConstructionBlockAccessList { func makeTestConstructionBAL() *ConstructionBlockAccessList {
return &ConstructionBlockAccessList{ return &ConstructionBlockAccessList{
map[common.Address]*ConstructionAccountAccess{ map[common.Address]*ConstructionAccountAccess{
common.BytesToAddress([]byte{0xff, 0xff}): { common.BytesToAddress([]byte{0xff, 0xff}): {
@ -90,7 +93,7 @@ func makeTestBAL() *ConstructionBlockAccessList {
// TestBALEncoding tests that a populated access list can be encoded/decoded correctly. // TestBALEncoding tests that a populated access list can be encoded/decoded correctly.
func TestBALEncoding(t *testing.T) { func TestBALEncoding(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
bal := makeTestBAL() bal := makeTestConstructionBAL()
err := bal.EncodeRLP(&buf) err := bal.EncodeRLP(&buf)
if err != nil { if err != nil {
t.Fatalf("encoding failed: %v\n", err) t.Fatalf("encoding failed: %v\n", err)
@ -107,22 +110,143 @@ func TestBALEncoding(t *testing.T) {
} }
} }
// TestBALEncoding tests that a populated access list can be encoded/decoded correctly. func makeTestAccountAccess(sort bool) AccountAccess {
func TestBALFullRLPEncoding(t *testing.T) { var (
var buf bytes.Buffer storageWrites []encodingSlotWrites
bal := makeTestBAL() storageReads [][32]byte
err := bal.EncodeRLP(&buf) balances []encodingBalanceChange
if err != nil { nonces []encodingAccountNonce
t.Fatalf("encoding failed: %v\n", err) )
for i := 0; i < 5; i++ {
slot := encodingSlotWrites{
Slot: testrand.Hash(),
}
for j := 0; j < 3; j++ {
slot.Accesses = append(slot.Accesses, encodingStorageWrite{
TxIdx: uint16(2 * j),
ValueAfter: testrand.Hash(),
})
}
if sort {
slices.SortFunc(slot.Accesses, func(a, b encodingStorageWrite) int {
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
})
}
storageWrites = append(storageWrites, slot)
} }
var dec BlockAccessList if sort {
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 10000000)); err != nil { slices.SortFunc(storageWrites, func(a, b encodingSlotWrites) int {
t.Fatalf("decoding failed: %v\n", err) return bytes.Compare(a.Slot[:], b.Slot[:])
})
} }
if dec.Hash() != bal.toEncodingObj().Hash() {
t.Fatalf("encoded block hash doesn't match decoded") for i := 0; i < 5; i++ {
storageReads = append(storageReads, testrand.Hash())
} }
if !equalBALs(bal.toEncodingObj(), &dec) { if sort {
t.Fatal("decoded BAL doesn't match") slices.SortFunc(storageReads, func(a, b [32]byte) int {
return bytes.Compare(a[:], b[:])
})
}
for i := 0; i < 5; i++ {
balances = append(balances, encodingBalanceChange{
TxIdx: uint16(2 * i),
Balance: [16]byte(testrand.Bytes(16)),
})
}
if sort {
slices.SortFunc(balances, func(a, b encodingBalanceChange) int {
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
})
}
for i := 0; i < 5; i++ {
nonces = append(nonces, encodingAccountNonce{
TxIdx: uint16(2 * i),
Nonce: uint64(i + 100),
})
}
if sort {
slices.SortFunc(nonces, func(a, b encodingAccountNonce) int {
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
})
}
return AccountAccess{
Address: [20]byte(testrand.Bytes(20)),
StorageWrites: storageWrites,
StorageReads: storageReads,
BalanceChanges: balances,
NonceChanges: nonces,
Code: []CodeChange{
{
TxIndex: 100,
Code: testrand.Bytes(256),
},
},
}
}
func makeTestBAL(sort bool) BlockAccessList {
list := BlockAccessList{}
for i := 0; i < 5; i++ {
list.Accesses = append(list.Accesses, makeTestAccountAccess(sort))
}
if sort {
slices.SortFunc(list.Accesses, func(a, b AccountAccess) int {
return bytes.Compare(a.Address[:], b.Address[:])
})
}
return list
}
func TestBlockAccessListCopy(t *testing.T) {
list := makeTestBAL(true)
cpy := list.Copy()
cpyCpy := cpy.Copy()
if !reflect.DeepEqual(list, cpy) {
t.Fatal("block access mismatch")
}
if !reflect.DeepEqual(cpy, cpyCpy) {
t.Fatal("block access mismatch")
}
// Make sure the mutations on copy won't affect the origin
for _, aa := range cpyCpy.Accesses {
for i := 0; i < len(aa.StorageReads); i++ {
aa.StorageReads[i] = [32]byte(testrand.Bytes(32))
}
}
if !reflect.DeepEqual(list, cpy) {
t.Fatal("block access mismatch")
}
}
func TestBlockAccessListValidation(t *testing.T) {
// Validate the block access list after RLP decoding
enc := makeTestBAL(true)
if err := enc.Validate(); err != nil {
t.Fatalf("Unexpected validation error: %v", err)
}
var buf bytes.Buffer
if err := enc.EncodeRLP(&buf); err != nil {
t.Fatalf("Unexpected encoding error: %v", err)
}
var dec BlockAccessList
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
t.Fatalf("Unexpected RLP-decode error: %v", err)
}
if err := dec.Validate(); err != nil {
t.Fatalf("Unexpected validation error: %v", err)
}
// Validate the derived block access list
cBAL := makeTestConstructionBAL()
listB := cBAL.toEncodingObj()
if err := listB.Validate(); err != nil {
t.Fatalf("Unexpected validation error: %v", err)
} }
} }