continued rework

This commit is contained in:
Jared Wasinger 2025-10-21 14:09:30 +08:00
parent 037d06e3b2
commit b4976948f2
4 changed files with 128 additions and 144 deletions

View file

@ -42,7 +42,7 @@ type idxAccessListBuilder struct {
accessesStack []map[common.Address]*constructionAccountAccess
}
func newAccessListBuilder() *idxAccessListBuilder {
func newIdxAccessListBuilder() *idxAccessListBuilder {
return &idxAccessListBuilder{
make(map[common.Address]*accountIdxPrestate),
[]map[common.Address]*constructionAccountAccess{
@ -233,7 +233,7 @@ func (a *idxAccessListBuilder) finalise() (*StateDiff, StateAccesses) {
// then emptied.
func (c *AccessListBuilder) FinaliseIdxChanges(idx uint16) {
pendingDiff, pendingAccesses := c.idxBuilder.finalise()
c.idxBuilder = newAccessListBuilder()
c.idxBuilder = newIdxAccessListBuilder()
// merge the set of state accesses/modifications into the access list:
// * any account or storage slot which was already recorded as a read
@ -245,40 +245,40 @@ func (c *AccessListBuilder) FinaliseIdxChanges(idx uint16) {
for addr, pendingAcctDiff := range pendingDiff.Mutations {
finalizedAcctChanges, ok := c.FinalizedAccesses[addr]
if !ok {
finalizedAcctChanges = &ConstructionAccountAccesses{}
finalizedAcctChanges = &constructionAccountAccesses{}
c.FinalizedAccesses[addr] = finalizedAcctChanges
}
if pendingAcctDiff.Nonce != nil {
if finalizedAcctChanges.NonceChanges == nil {
finalizedAcctChanges.NonceChanges = make(map[uint16]uint64)
if finalizedAcctChanges.nonceChanges == nil {
finalizedAcctChanges.nonceChanges = make(map[uint16]uint64)
}
finalizedAcctChanges.NonceChanges[idx] = *pendingAcctDiff.Nonce
finalizedAcctChanges.nonceChanges[idx] = *pendingAcctDiff.Nonce
}
if pendingAcctDiff.Balance != nil {
if finalizedAcctChanges.BalanceChanges == nil {
finalizedAcctChanges.BalanceChanges = make(map[uint16]*uint256.Int)
if finalizedAcctChanges.balanceChanges == nil {
finalizedAcctChanges.balanceChanges = make(map[uint16]*uint256.Int)
}
finalizedAcctChanges.BalanceChanges[idx] = pendingAcctDiff.Balance
finalizedAcctChanges.balanceChanges[idx] = pendingAcctDiff.Balance
}
if pendingAcctDiff.Code != nil {
if finalizedAcctChanges.CodeChanges == nil {
finalizedAcctChanges.CodeChanges = make(map[uint16]CodeChange)
if finalizedAcctChanges.codeChanges == nil {
finalizedAcctChanges.codeChanges = make(map[uint16]CodeChange)
}
finalizedAcctChanges.CodeChanges[idx] = CodeChange{idx, pendingAcctDiff.Code}
finalizedAcctChanges.codeChanges[idx] = CodeChange{idx, pendingAcctDiff.Code}
}
if pendingAcctDiff.StorageWrites != nil {
if finalizedAcctChanges.StorageWrites == nil {
finalizedAcctChanges.StorageWrites = make(map[common.Hash]map[uint16]common.Hash)
if finalizedAcctChanges.storageWrites == nil {
finalizedAcctChanges.storageWrites = make(map[common.Hash]map[uint16]common.Hash)
}
for key, val := range pendingAcctDiff.StorageWrites {
if _, ok := finalizedAcctChanges.StorageWrites[key]; !ok {
finalizedAcctChanges.StorageWrites[key] = make(map[uint16]common.Hash)
if _, ok := finalizedAcctChanges.storageWrites[key]; !ok {
finalizedAcctChanges.storageWrites[key] = make(map[uint16]common.Hash)
}
finalizedAcctChanges.StorageWrites[key][idx] = val
finalizedAcctChanges.storageWrites[key][idx] = val
if _, ok := finalizedAcctChanges.StorageReads[key]; ok {
delete(finalizedAcctChanges.StorageReads, key)
if _, ok := finalizedAcctChanges.storageReads[key]; ok {
delete(finalizedAcctChanges.storageReads, key)
}
}
}
@ -288,18 +288,18 @@ func (c *AccessListBuilder) FinaliseIdxChanges(idx uint16) {
for addr, pendingAccountAccesses := range pendingAccesses {
finalizedAcctAccesses, ok := c.FinalizedAccesses[addr]
if !ok {
finalizedAcctAccesses = &ConstructionAccountAccesses{}
finalizedAcctAccesses = &constructionAccountAccesses{}
c.FinalizedAccesses[addr] = finalizedAcctAccesses
}
for key := range pendingAccountAccesses {
if _, ok := finalizedAcctAccesses.StorageWrites[key]; ok {
if _, ok := finalizedAcctAccesses.storageWrites[key]; ok {
continue
}
if finalizedAcctAccesses.StorageReads == nil {
finalizedAcctAccesses.StorageReads = make(map[common.Hash]struct{})
if finalizedAcctAccesses.storageReads == nil {
finalizedAcctAccesses.storageReads = make(map[common.Hash]struct{})
}
finalizedAcctAccesses.StorageReads[key] = struct{}{}
finalizedAcctAccesses.storageReads[key] = struct{}{}
}
}
c.lastFinalizedMutations = pendingDiff
@ -342,7 +342,7 @@ type CodeChange struct {
Code []byte `json:"code,omitempty"`
}
// ConstructionAccountAccesses contains all state mutations which were applied
// constructionAccountAccesses contains all state mutations which were applied
// to a single account during the execution of a block.
// It contains the final values for the account fields and storage slots which
// were mutated, indexed by where they occurred:
@ -352,17 +352,19 @@ type CodeChange struct {
//
// It also contains a set of storage slot accesses for state which was accessed
// but not modified. State accesses are not keyed by an index where they occurred.
type ConstructionAccountAccesses struct {
// StorageWrites contain mutated storage slots and their values.
type constructionAccountAccesses struct {
// storageWrites contain mutated storage slots and their values.
// It is indexed by storage slot -> access list index -> post-state value
StorageWrites map[common.Hash]map[uint16]common.Hash
storageWrites map[common.Hash]map[uint16]common.Hash
StorageReads map[common.Hash]struct{}
BalanceChanges map[uint16]*uint256.Int
NonceChanges map[uint16]uint64
CodeChanges map[uint16]CodeChange
storageReads map[common.Hash]struct{}
balanceChanges map[uint16]*uint256.Int
nonceChanges map[uint16]uint64
codeChanges map[uint16]CodeChange
}
type ConstructionBlockAccessList map[common.Address]*constructionAccountAccesses
// constructionAccountAccess contains fields for an account which were modified
// during execution of the current access list index.
// It also accumulates a set of storage slots which were accessed but not
@ -477,7 +479,7 @@ func (c *constructionAccountAccess) NonceChange(cur uint64) {
// AccessListBuilder is used to build an EIP-7928 block access list
type AccessListBuilder struct {
FinalizedAccesses map[common.Address]*ConstructionAccountAccesses
FinalizedAccesses ConstructionBlockAccessList
idxBuilder *idxAccessListBuilder
@ -488,8 +490,8 @@ type AccessListBuilder struct {
// NewAccessListBuilder instantiates an empty access list.
func NewAccessListBuilder() *AccessListBuilder {
return &AccessListBuilder{
make(map[common.Address]*ConstructionAccountAccesses),
newAccessListBuilder(),
make(map[common.Address]*constructionAccountAccesses),
newIdxAccessListBuilder(),
nil,
nil,
}
@ -499,24 +501,24 @@ func NewAccessListBuilder() *AccessListBuilder {
func (c *AccessListBuilder) Copy() *AccessListBuilder {
res := NewAccessListBuilder()
for addr, aa := range c.FinalizedAccesses {
var aaCopy ConstructionAccountAccesses
var aaCopy constructionAccountAccesses
slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites))
for key, m := range aa.StorageWrites {
slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.storageWrites))
for key, m := range aa.storageWrites {
slotWrites[key] = maps.Clone(m)
}
aaCopy.StorageWrites = slotWrites
aaCopy.StorageReads = maps.Clone(aa.StorageReads)
aaCopy.storageWrites = slotWrites
aaCopy.storageReads = maps.Clone(aa.storageReads)
balances := make(map[uint16]*uint256.Int, len(aa.BalanceChanges))
for index, balance := range aa.BalanceChanges {
balances := make(map[uint16]*uint256.Int, len(aa.balanceChanges))
for index, balance := range aa.balanceChanges {
balances[index] = balance.Clone()
}
aaCopy.BalanceChanges = balances
aaCopy.NonceChanges = maps.Clone(aa.NonceChanges)
aaCopy.balanceChanges = balances
aaCopy.nonceChanges = maps.Clone(aa.nonceChanges)
codeChangesCopy := make(map[uint16]CodeChange)
for idx, codeChange := range aa.CodeChanges {
for idx, codeChange := range aa.codeChanges {
codeChangesCopy[idx] = CodeChange{
TxIdx: idx,
Code: bytes.Clone(codeChange.Code),

View file

@ -19,7 +19,6 @@ package bal
import (
"bytes"
"cmp"
"encoding/json"
"errors"
"fmt"
"io"
@ -67,15 +66,6 @@ func (e *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
return nil
}
func (e *BlockAccessList) String() string {
var res bytes.Buffer
enc := json.NewEncoder(&res)
enc.SetIndent("", " ")
// TODO: check error
enc.Encode(e)
return res.String()
}
// Validate returns an error if the contents of the access list are not ordered
// according to the spec or any code changes are contained which exceed protocol
// max code size.
@ -141,7 +131,7 @@ func (e *encodingSlotWrites) validate() error {
return errors.New("storage write tx indices not in order")
}
// AccountAccess is the encoding format of ConstructionAccountAccesses.
// AccountAccess is the encoding format of constructionAccountAccesses.
type AccountAccess struct {
Address common.Address `json:"address,omitempty"` // 20-byte Ethereum address
StorageChanges []encodingSlotWrites `json:"storageChanges,omitempty"` // Storage changes (slot -> [tx_index -> new_value])
@ -166,7 +156,6 @@ func (e *AccountAccess) validate() error {
return err
}
}
// test case ideas: keys in both read/writes, duplicate keys in either read/writes
// ensure that the read and write key sets are distinct
readKeys := make(map[common.Hash]struct{})
writeKeys := make(map[common.Hash]struct{})
@ -211,7 +200,6 @@ func (e *AccountAccess) validate() error {
return errors.New("nonce changes not in ascending order by tx index")
}
// Convert code change
for _, codeChange := range e.CodeChanges {
if len(codeChange.Code) > params.MaxCodeSize {
return fmt.Errorf("code change contained oversized code")
@ -244,16 +232,9 @@ func (e *AccountAccess) Copy() AccountAccess {
return res
}
// EncodeRLP returns the RLP-encoded access list
func (c *AccessListBuilder) EncodeRLP(wr io.Writer) error {
return c.ToEncodingObj().EncodeRLP(wr)
}
var _ rlp.Encoder = &AccessListBuilder{}
// toEncodingObj creates an instance of the ConstructionAccountAccesses of the type that is
// toEncodingObj creates an instance of the constructionAccountAccesses of the type that is
// used as input for the encoding.
func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) AccountAccess {
func (a *constructionAccountAccesses) toEncodingObj(addr common.Address) AccountAccess {
res := AccountAccess{
Address: addr,
StorageChanges: make([]encodingSlotWrites, 0),
@ -264,13 +245,13 @@ func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) Account
}
// Convert write slots
writeSlots := slices.Collect(maps.Keys(a.StorageWrites))
writeSlots := slices.Collect(maps.Keys(a.storageWrites))
slices.SortFunc(writeSlots, common.Hash.Cmp)
for _, slot := range writeSlots {
var obj encodingSlotWrites
obj.Slot = slot
slotWrites := a.StorageWrites[slot]
slotWrites := a.storageWrites[slot]
obj.Accesses = make([]encodingStorageWrite, 0, len(slotWrites))
indices := slices.Collect(maps.Keys(slotWrites))
@ -285,39 +266,39 @@ func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) Account
}
// Convert read slots
readSlots := slices.Collect(maps.Keys(a.StorageReads))
readSlots := slices.Collect(maps.Keys(a.storageReads))
slices.SortFunc(readSlots, common.Hash.Cmp)
for _, slot := range readSlots {
res.StorageReads = append(res.StorageReads, slot)
}
// Convert balance changes
balanceIndices := slices.Collect(maps.Keys(a.BalanceChanges))
balanceIndices := slices.Collect(maps.Keys(a.balanceChanges))
slices.SortFunc(balanceIndices, cmp.Compare[uint16])
for _, idx := range balanceIndices {
res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{
TxIdx: idx,
Balance: new(uint256.Int).Set(a.BalanceChanges[idx]),
Balance: new(uint256.Int).Set(a.balanceChanges[idx]),
})
}
// Convert nonce changes
nonceIndices := slices.Collect(maps.Keys(a.NonceChanges))
nonceIndices := slices.Collect(maps.Keys(a.nonceChanges))
slices.SortFunc(nonceIndices, cmp.Compare[uint16])
for _, idx := range nonceIndices {
res.NonceChanges = append(res.NonceChanges, encodingAccountNonce{
TxIdx: idx,
Nonce: a.NonceChanges[idx],
Nonce: a.nonceChanges[idx],
})
}
// Convert code change
codeChangeIdxs := slices.Collect(maps.Keys(a.CodeChanges))
codeChangeIdxs := slices.Collect(maps.Keys(a.codeChanges))
slices.SortFunc(codeChangeIdxs, cmp.Compare[uint16])
for _, idx := range codeChangeIdxs {
res.CodeChanges = append(res.CodeChanges, CodeChange{
idx,
bytes.Clone(a.CodeChanges[idx].Code),
bytes.Clone(a.codeChanges[idx].Code),
})
}
return res
@ -325,16 +306,16 @@ func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) Account
// ToEncodingObj returns an instance of the access list expressed as the type
// which is used as input for the encoding/decoding.
func (c *AccessListBuilder) ToEncodingObj() *BlockAccessList {
func (c ConstructionBlockAccessList) ToEncodingObj() *BlockAccessList {
var addresses []common.Address
for addr := range c.FinalizedAccesses {
for addr := range c {
addresses = append(addresses, addr)
}
slices.SortFunc(addresses, common.Address.Cmp)
var res BlockAccessList
for _, addr := range addresses {
res = append(res, c.FinalizedAccesses[addr].toEncodingObj(addr))
res = append(res, c[addr].toEncodingObj(addr))
}
return &res
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
func (c *ContractCode) MarshalJSON() ([]byte, error) {
@ -12,23 +13,21 @@ func (c *ContractCode) MarshalJSON() ([]byte, error) {
return json.Marshal(hexStr)
}
func (e encodingBalanceChange) MarshalJSON() ([]byte, error) {
type Alias encodingBalanceChange
return json.Marshal(&struct {
TxIdx string `json:"txIndex"`
*Alias
Balance *uint256.Int
}{
TxIdx: fmt.Sprintf("0x%x", e.TxIdx),
Alias: (*Alias)(&e),
Balance: e.Balance,
})
}
func (e *encodingBalanceChange) UnmarshalJSON(data []byte) error {
type Alias encodingBalanceChange
aux := &struct {
TxIdx string `json:"txIndex"`
*Alias
Balance *uint256.Int
}{
Alias: (*Alias)(e),
Balance: e.Balance,
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
@ -41,27 +40,20 @@ func (e *encodingBalanceChange) UnmarshalJSON(data []byte) error {
return nil
}
func (e encodingAccountNonce) MarshalJSON() ([]byte, error) {
type Alias encodingAccountNonce
return json.Marshal(&struct {
TxIdx string `json:"txIndex"`
Nonce string `json:"nonce"`
*Alias
}{
TxIdx: fmt.Sprintf("0x%x", e.TxIdx),
Nonce: fmt.Sprintf("0x%x", e.Nonce),
Alias: (*Alias)(&e),
})
}
func (e *encodingAccountNonce) UnmarshalJSON(data []byte) error {
type Alias encodingAccountNonce
aux := &struct {
TxIdx string `json:"txIndex"`
Nonce string `json:"nonce"`
*Alias
}{
Alias: (*Alias)(e),
}
}{}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
@ -105,3 +97,17 @@ func (b BlockAccessList) MarshalJSON() ([]byte, error) {
}
return json.Marshal(hexutil.Bytes(rlpBytes))
}
func (b BlockAccessList) String() string {
aux := []AccountAccess{}
for _, access := range b {
aux = append(aux, access)
}
res, err := json.MarshalIndent(aux, "", " ")
if err != nil {
panic(err)
}
return string(res)
}

View file

@ -36,11 +36,10 @@ func equalBALs(a *BlockAccessList, b *BlockAccessList) bool {
return true
}
func makeTestConstructionBAL() *AccessListBuilder {
return &AccessListBuilder{
map[common.Address]*ConstructionAccountAccesses{
func makeTestConstructionBAL() *ConstructionBlockAccessList {
return &ConstructionBlockAccessList{
common.BytesToAddress([]byte{0xff, 0xff}): {
StorageWrites: map[common.Hash]map[uint16]common.Hash{
storageWrites: map[common.Hash]map[uint16]common.Hash{
common.BytesToHash([]byte{0x01}): {
1: common.BytesToHash([]byte{1, 2, 3, 4}),
2: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6}),
@ -49,24 +48,24 @@ func makeTestConstructionBAL() *AccessListBuilder {
20: common.BytesToHash([]byte{1, 2, 3, 4}),
},
},
StorageReads: map[common.Hash]struct{}{
storageReads: map[common.Hash]struct{}{
common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7}): {},
},
BalanceChanges: map[uint16]*uint256.Int{
balanceChanges: map[uint16]*uint256.Int{
1: uint256.NewInt(100),
2: uint256.NewInt(500),
},
NonceChanges: map[uint16]uint64{
nonceChanges: map[uint16]uint64{
1: 2,
2: 6,
},
CodeChanges: map[uint16]CodeChange{0: {
codeChanges: map[uint16]CodeChange{0: {
TxIdx: 0,
Code: common.Hex2Bytes("deadbeef"),
}},
},
common.BytesToAddress([]byte{0xff, 0xff, 0xff}): {
StorageWrites: map[common.Hash]map[uint16]common.Hash{
storageWrites: map[common.Hash]map[uint16]common.Hash{
common.BytesToHash([]byte{0x01}): {
2: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6}),
3: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
@ -75,18 +74,17 @@ func makeTestConstructionBAL() *AccessListBuilder {
21: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
},
},
StorageReads: map[common.Hash]struct{}{
storageReads: map[common.Hash]struct{}{
common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {},
},
BalanceChanges: map[uint16]*uint256.Int{
balanceChanges: map[uint16]*uint256.Int{
2: uint256.NewInt(100),
3: uint256.NewInt(500),
},
NonceChanges: map[uint16]uint64{
nonceChanges: map[uint16]uint64{
1: 2,
},
},
},
}
}
@ -94,7 +92,7 @@ func makeTestConstructionBAL() *AccessListBuilder {
func TestBALEncoding(t *testing.T) {
var buf bytes.Buffer
bal := makeTestConstructionBAL()
err := bal.EncodeRLP(&buf)
err := bal.ToEncodingObj().EncodeRLP(&buf)
if err != nil {
t.Fatalf("encoding failed: %v\n", err)
}
@ -250,6 +248,3 @@ func TestBlockAccessListValidation(t *testing.T) {
t.Fatalf("Unexpected validation error: %v", err)
}
}
// BALReader test ideas
// * BAL which doesn't have any pre-tx system contracts should return an empty state diff at idx 0