core/types/bal: change code change type to list (#33774)

To align with the latest spec of EIP-7928:

```
# CodeChange: [block_access_index, new_code]
CodeChange = [BlockAccessIndex, Bytecode]
```
This commit is contained in:
rjl493456442 2026-02-24 21:53:20 +08:00 committed by GitHub
parent 01083736c8
commit 199ac16e07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 54 deletions

View file

@ -24,13 +24,6 @@ import (
"github.com/holiman/uint256"
)
// CodeChange contains the runtime bytecode deployed at an address and the
// transaction index where the deployment took place.
type CodeChange struct {
TxIndex uint16
Code []byte `json:"code,omitempty"`
}
// ConstructionAccountAccess contains post-block account state for mutations as well as
// all storage keys that were read during execution. It is used when building block
// access list during execution.
@ -55,9 +48,9 @@ type ConstructionAccountAccess struct {
// by tx index.
NonceChanges map[uint16]uint64 `json:"nonceChanges,omitempty"`
// CodeChange is only set for contract accounts which were deployed in
// the block.
CodeChange *CodeChange `json:"codeChange,omitempty"`
// CodeChange contains the post-state contract code of an account keyed
// by tx index.
CodeChange map[uint16][]byte `json:"codeChange,omitempty"`
}
// NewConstructionAccountAccess initializes the account access object.
@ -67,6 +60,7 @@ func NewConstructionAccountAccess() *ConstructionAccountAccess {
StorageReads: make(map[common.Hash]struct{}),
BalanceChanges: make(map[uint16]*uint256.Int),
NonceChanges: make(map[uint16]uint64),
CodeChange: make(map[uint16][]byte),
}
}
@ -120,10 +114,8 @@ func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex
if _, ok := b.Accounts[address]; !ok {
b.Accounts[address] = NewConstructionAccountAccess()
}
b.Accounts[address].CodeChange = &CodeChange{
TxIndex: txIndex,
Code: bytes.Clone(code),
}
// TODO(rjl493456442) is it essential to deep-copy the code?
b.Accounts[address].CodeChange[txIndex] = bytes.Clone(code)
}
// NonceChange records tx post-state nonce of any contract-like accounts whose
@ -170,12 +162,11 @@ func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
aaCopy.BalanceChanges = balances
aaCopy.NonceChanges = maps.Clone(aa.NonceChanges)
if aa.CodeChange != nil {
aaCopy.CodeChange = &CodeChange{
TxIndex: aa.CodeChange.TxIndex,
Code: bytes.Clone(aa.CodeChange.Code),
}
codes := make(map[uint16][]byte, len(aa.CodeChange))
for index, code := range aa.CodeChange {
codes[index] = bytes.Clone(code)
}
aaCopy.CodeChange = codes
res.Accounts[addr] = &aaCopy
}
return &res

View file

@ -119,6 +119,13 @@ func (e *encodingSlotWrites) validate() error {
return errors.New("storage write tx indices not in order")
}
// encodingCodeChange contains the runtime bytecode deployed at an address
// and the transaction index where the deployment took place.
type encodingCodeChange struct {
TxIndex uint16 `ssz-size:"2"`
Code []byte `ssz-max:"300000"` // TODO(rjl493456442) shall we put the limit here? The limit will be increased gradually
}
// AccountAccess is the encoding format of ConstructionAccountAccess.
type AccountAccess struct {
Address [20]byte `ssz-size:"20"` // 20-byte Ethereum address
@ -126,7 +133,7 @@ type AccountAccess struct {
StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys
BalanceChanges []encodingBalanceChange `ssz-max:"300000"` // Balance changes ([tx_index -> post_balance])
NonceChanges []encodingAccountNonce `ssz-max:"300000"` // Nonce changes ([tx_index -> new_nonce])
Code []CodeChange `ssz-max:"1"` // Code changes ([tx_index -> new_code])
CodeChanges []encodingCodeChange `ssz-max:"300000"` // Code changes ([tx_index -> new_code])
}
// validate converts the account accesses out of encoding format.
@ -166,9 +173,16 @@ func (e *AccountAccess) validate() error {
return errors.New("nonce changes not in ascending order by tx index")
}
// Convert code change
if len(e.Code) == 1 {
if len(e.Code[0].Code) > params.MaxCodeSize {
// Check the code changes are sorted in order
if !slices.IsSortedFunc(e.CodeChanges, func(a, b encodingCodeChange) int {
return cmp.Compare[uint16](a.TxIndex, b.TxIndex)
}) {
return errors.New("code changes not in ascending order by tx index")
}
for _, change := range e.CodeChanges {
// TODO(rjl493456442): This check should be fork-aware, since the limit may
// differ across forks.
if len(change.Code) > params.MaxCodeSize {
return errors.New("code change contained oversized code")
}
}
@ -182,6 +196,8 @@ func (e *AccountAccess) Copy() AccountAccess {
StorageReads: slices.Clone(e.StorageReads),
BalanceChanges: slices.Clone(e.BalanceChanges),
NonceChanges: slices.Clone(e.NonceChanges),
StorageWrites: make([]encodingSlotWrites, 0, len(e.StorageWrites)),
CodeChanges: make([]encodingCodeChange, 0, len(e.CodeChanges)),
}
for _, storageWrite := range e.StorageWrites {
res.StorageWrites = append(res.StorageWrites, encodingSlotWrites{
@ -189,13 +205,11 @@ func (e *AccountAccess) Copy() AccountAccess {
Accesses: slices.Clone(storageWrite.Accesses),
})
}
if len(e.Code) == 1 {
res.Code = []CodeChange{
{
e.Code[0].TxIndex,
bytes.Clone(e.Code[0].Code),
},
}
for _, codeChange := range e.CodeChanges {
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
TxIndex: codeChange.TxIndex,
Code: bytes.Clone(codeChange.Code),
})
}
return res
}
@ -212,11 +226,11 @@ var _ rlp.Encoder = &ConstructionBlockAccessList{}
func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess {
res := AccountAccess{
Address: addr,
StorageWrites: make([]encodingSlotWrites, 0),
StorageReads: make([][32]byte, 0),
BalanceChanges: make([]encodingBalanceChange, 0),
NonceChanges: make([]encodingAccountNonce, 0),
Code: nil,
StorageWrites: make([]encodingSlotWrites, 0, len(a.StorageWrites)),
StorageReads: make([][32]byte, 0, len(a.StorageReads)),
BalanceChanges: make([]encodingBalanceChange, 0, len(a.BalanceChanges)),
NonceChanges: make([]encodingAccountNonce, 0, len(a.NonceChanges)),
CodeChanges: make([]encodingCodeChange, 0, len(a.CodeChange)),
}
// Convert write slots
@ -268,13 +282,13 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
}
// Convert code change
if a.CodeChange != nil {
res.Code = []CodeChange{
{
a.CodeChange.TxIndex,
bytes.Clone(a.CodeChange.Code),
},
}
codeIndices := slices.Collect(maps.Keys(a.CodeChange))
slices.SortFunc(codeIndices, cmp.Compare[uint16])
for _, idx := range codeIndices {
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
TxIndex: idx,
Code: a.CodeChange[idx],
})
}
return res
}
@ -327,9 +341,9 @@ func (e *BlockAccessList) PrettyPrint() string {
printWithIndent(2, fmt.Sprintf("%d: %d", change.TxIdx, change.Nonce))
}
if len(accountDiff.Code) > 0 {
printWithIndent(1, "code:")
printWithIndent(2, fmt.Sprintf("%d: %x", accountDiff.Code[0].TxIndex, accountDiff.Code[0].Code))
printWithIndent(1, "code changes:")
for _, change := range accountDiff.CodeChanges {
printWithIndent(2, fmt.Sprintf("%d: %x", change.TxIndex, change.Code))
}
}
return res.String()

View file

@ -49,7 +49,7 @@ func (obj *BlockAccessList) EncodeRLP(_w io.Writer) error {
}
w.ListEnd(_tmp15)
_tmp18 := w.List()
for _, _tmp19 := range _tmp2.Code {
for _, _tmp19 := range _tmp2.CodeChanges {
_tmp20 := w.List()
w.WriteUint64(uint64(_tmp19.TxIndex))
w.WriteBytes(_tmp19.Code)
@ -228,13 +228,13 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
return err
}
_tmp2.NonceChanges = _tmp17
// Code:
var _tmp21 []CodeChange
// CodeChanges:
var _tmp21 []encodingCodeChange
if _, err := dec.List(); err != nil {
return err
}
for dec.MoreDataInList() {
var _tmp22 CodeChange
var _tmp22 encodingCodeChange
{
if _, err := dec.List(); err != nil {
return err
@ -260,7 +260,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
if err := dec.ListEnd(); err != nil {
return err
}
_tmp2.Code = _tmp21
_tmp2.CodeChanges = _tmp21
if err := dec.ListEnd(); err != nil {
return err
}

View file

@ -60,9 +60,8 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
1: 2,
2: 6,
},
CodeChange: &CodeChange{
TxIndex: 0,
Code: common.Hex2Bytes("deadbeef"),
CodeChange: map[uint16][]byte{
0: common.Hex2Bytes("deadbeef"),
},
},
common.BytesToAddress([]byte{0xff, 0xff, 0xff}): {
@ -85,6 +84,9 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
NonceChanges: map[uint16]uint64{
1: 2,
},
CodeChange: map[uint16][]byte{
0: common.Hex2Bytes("deadbeef"),
},
},
},
}
@ -179,7 +181,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
StorageReads: storageReads,
BalanceChanges: balances,
NonceChanges: nonces,
Code: []CodeChange{
CodeChanges: []encodingCodeChange{
{
TxIndex: 100,
Code: testrand.Bytes(256),