diff --git a/core/types/bal/bal.go b/core/types/bal/bal.go index add820f799..193ff55f19 100644 --- a/core/types/bal/bal.go +++ b/core/types/bal/bal.go @@ -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), diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go index 515033cafb..53bc23098a 100644 --- a/core/types/bal/bal_encoding.go +++ b/core/types/bal/bal_encoding.go @@ -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 } diff --git a/core/types/bal/bal_encoding_json.go b/core/types/bal/bal_encoding_json.go index bf25345b5e..55e798634b 100644 --- a/core/types/bal/bal_encoding_json.go +++ b/core/types/bal/bal_encoding_json.go @@ -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 + TxIdx string `json:"txIndex"` + Balance *uint256.Int }{ - TxIdx: fmt.Sprintf("0x%x", e.TxIdx), - Alias: (*Alias)(&e), + TxIdx: fmt.Sprintf("0x%x", e.TxIdx), + Balance: e.Balance, }) } func (e *encodingBalanceChange) UnmarshalJSON(data []byte) error { - type Alias encodingBalanceChange aux := &struct { - TxIdx string `json:"txIndex"` - *Alias + TxIdx string `json:"txIndex"` + 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) +} diff --git a/core/types/bal/bal_test.go b/core/types/bal/bal_test.go index 1e49ada31a..1ec23124c6 100644 --- a/core/types/bal/bal_test.go +++ b/core/types/bal/bal_test.go @@ -36,56 +36,54 @@ func equalBALs(a *BlockAccessList, b *BlockAccessList) bool { return true } -func makeTestConstructionBAL() *AccessListBuilder { - return &AccessListBuilder{ - map[common.Address]*ConstructionAccountAccesses{ - common.BytesToAddress([]byte{0xff, 0xff}): { - 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}), - }, - common.BytesToHash([]byte{0x10}): { - 20: common.BytesToHash([]byte{1, 2, 3, 4}), - }, +func makeTestConstructionBAL() *ConstructionBlockAccessList { + return &ConstructionBlockAccessList{ + common.BytesToAddress([]byte{0xff, 0xff}): { + 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}), }, - StorageReads: map[common.Hash]struct{}{ - common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7}): {}, + common.BytesToHash([]byte{0x10}): { + 20: common.BytesToHash([]byte{1, 2, 3, 4}), }, - BalanceChanges: map[uint16]*uint256.Int{ - 1: uint256.NewInt(100), - 2: uint256.NewInt(500), - }, - NonceChanges: map[uint16]uint64{ - 1: 2, - 2: 6, - }, - 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{ - 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}), - }, - common.BytesToHash([]byte{0x10}): { - 21: common.BytesToHash([]byte{1, 2, 3, 4, 5}), - }, + storageReads: map[common.Hash]struct{}{ + common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7}): {}, + }, + balanceChanges: map[uint16]*uint256.Int{ + 1: uint256.NewInt(100), + 2: uint256.NewInt(500), + }, + nonceChanges: map[uint16]uint64{ + 1: 2, + 2: 6, + }, + 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{ + 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}), }, - StorageReads: map[common.Hash]struct{}{ - common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {}, - }, - BalanceChanges: map[uint16]*uint256.Int{ - 2: uint256.NewInt(100), - 3: uint256.NewInt(500), - }, - NonceChanges: map[uint16]uint64{ - 1: 2, + common.BytesToHash([]byte{0x10}): { + 21: common.BytesToHash([]byte{1, 2, 3, 4, 5}), }, }, + storageReads: map[common.Hash]struct{}{ + common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {}, + }, + balanceChanges: map[uint16]*uint256.Int{ + 2: uint256.NewInt(100), + 3: uint256.NewInt(500), + }, + 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