From 68d7bc93b8d385f9acff04aa3e19a1ecdbf19c8e Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 1 Feb 2021 16:51:25 +0100 Subject: [PATCH 01/10] core/state: added AccessList() function to statedb --- core/state/statedb.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/state/statedb.go b/core/state/statedb.go index 2e5d6e47c83c..11b4465afe36 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1043,3 +1043,20 @@ func (s *StateDB) AddressInAccessList(addr common.Address) bool { func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { return s.accessList.Contains(addr, slot) } + +func (s *StateDB) AccessList() *types.AccessList { + list := s.accessList.Copy() + acl := make([]types.AccessTuple, 0, len(list.addresses)) + for addr, idx := range list.addresses { + var tuple types.AccessTuple + tuple.Address = &addr + keys := make([]*common.Hash, 0, len(list.slots[idx])) + for key, _ := range list.slots[idx] { + tuple.StorageKeys = append(tuple.StorageKeys, &key) + } + tuple.StorageKeys = keys + acl = append(acl, tuple) + } + cast := types.AccessList(acl) + return &cast +} From 671da6bf753098d39e8a9c8d5be57e11a8c097eb Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 8 Feb 2021 13:15:17 +0100 Subject: [PATCH 02/10] core/state: added UnprepareAccessList to remove sender, recipient,... --- core/state/journal.go | 11 +++++++++++ core/state/statedb.go | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/core/state/journal.go b/core/state/journal.go index 2070f3087554..fe217193254e 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -138,6 +138,9 @@ type ( address *common.Address slot *common.Hash } + accessListDeleteAccountChange struct { + address *common.Address + } ) func (ch createObjectChange) revert(s *StateDB) { @@ -260,6 +263,14 @@ func (ch accessListAddAccountChange) dirtied() *common.Address { return nil } +func (ch accessListDeleteAccountChange) revert(s *StateDB) { + s.accessList.AddAddress(*ch.address) +} + +func (ch accessListDeleteAccountChange) dirtied() *common.Address { + return nil +} + func (ch accessListAddSlotChange) revert(s *StateDB) { s.accessList.DeleteSlot(*ch.address, *ch.slot) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 11b4465afe36..1fd3d564b191 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1034,6 +1034,27 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { } } +// UnprepareAccessList unprepares the access list of the statedb. +// It reverts the preparation steps from EIP-2929 +// - Delete sender from access list (2929) +// - Delete receiver from access list (2929) +// - Delete precompiles from access list (2929) +func (s *StateDB) UnprepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address) { + s.DeleteAddressFromAccessList(sender) + if dst != nil { + s.DeleteAddressFromAccessList(*dst) + } + for _, addr := range precompiles { + s.DeleteAddressFromAccessList(addr) + } +} + +// DeleteAddressFromAccessList deletes the given address from the access list +func (s *StateDB) DeleteAddressFromAccessList(addr common.Address) { + s.accessList.DeleteAddress(addr) + s.journal.append(accessListDeleteAccountChange{&addr}) +} + // AddressInAccessList returns true if the given address is in the access list. func (s *StateDB) AddressInAccessList(addr common.Address) bool { return s.accessList.ContainsAddress(addr) @@ -1044,6 +1065,9 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre return s.accessList.Contains(addr, slot) } +// AccessList returns the current access list for the StateDB. +// If the precompiles, sender and receiver are to be removed from the access list, +// a prior call to `UnprepareAccessList` is required. func (s *StateDB) AccessList() *types.AccessList { list := s.accessList.Copy() acl := make([]types.AccessTuple, 0, len(list.addresses)) From ab97827e74b240e794568ddbfbb859882d1813db Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 8 Feb 2021 14:03:57 +0100 Subject: [PATCH 03/10] eth: add EthAPIBackend.AccessList() to generate a proper access list --- eth/api_backend.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/eth/api_backend.go b/eth/api_backend.go index 2569972e527f..fbd1f28df9ff 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -19,6 +19,7 @@ package eth import ( "context" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -343,3 +344,25 @@ func (b *EthAPIBackend) StatesInRange(ctx context.Context, fromBlock *types.Bloc func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) { return b.eth.stateAtTransaction(block, txIndex, reexec) } + +func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reexec uint64, tx *types.Transaction) (*types.AccessList, error) { + statedb, release, err := b.eth.stateAtBlock(block, reexec) + defer release() + if err != nil { + return nil, err + } + signer := types.MakeSigner(b.eth.blockchain.Config(), block.Number()) + msg, err := tx.AsMessage(signer) + if err != nil { + return nil, err + } + txContext := core.NewEVMTxContext(msg) + context := core.NewEVMBlockContext(block.Header(), b.eth.blockchain, nil) + // Not yet the searched for transaction, execute on top of the current state + vmenv := vm.NewEVM(context, txContext, statedb, b.eth.blockchain.Config(), vm.Config{}) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + return nil, fmt.Errorf("failed to apply transaction: %v", tx.Hash(), err) + } + statedb.UnprepareAccessList(msg.From(), msg.To(), vmenv.ActivePrecompiles()) + return statedb.AccessList(), nil +} From d735b85dccff3ed177422d553c11f694c5911356 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 9 Feb 2021 18:14:05 +0100 Subject: [PATCH 04/10] =?UTF-8?q?core/state:=20added=20proper=20access?= =?UTF-8?q?=C3=83list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/state/statedb.go | 24 +++++++++++++++++++++--- core/vm/interface.go | 2 ++ eth/api_backend.go | 7 +++++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 1fd3d564b191..f2f9773871d7 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1065,10 +1065,10 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre return s.accessList.Contains(addr, slot) } -// AccessList returns the current access list for the StateDB. +// CurrentAccessList returns the current access list for the StateDB. // If the precompiles, sender and receiver are to be removed from the access list, // a prior call to `UnprepareAccessList` is required. -func (s *StateDB) AccessList() *types.AccessList { +func (s *StateDB) CurrentAccessList() *types.AccessList { list := s.accessList.Copy() acl := make([]types.AccessTuple, 0, len(list.addresses)) for addr, idx := range list.addresses { @@ -1076,7 +1076,25 @@ func (s *StateDB) AccessList() *types.AccessList { tuple.Address = &addr keys := make([]*common.Hash, 0, len(list.slots[idx])) for key, _ := range list.slots[idx] { - tuple.StorageKeys = append(tuple.StorageKeys, &key) + keys = append(keys, &key) + } + tuple.StorageKeys = keys + acl = append(acl, tuple) + } + cast := types.AccessList(acl) + return &cast +} + +// AccessList returns a list of dirty slots and addresses +func (s *StateDB) AccessList() *types.AccessList { + acl := make([]types.AccessTuple, 0, len(s.journal.dirties)) + for addr := range s.journal.dirties { + var tuple types.AccessTuple + tuple.Address = &addr + obj := s.stateObjects[addr] + keys := make([]*common.Hash, 0, len(obj.dirtyStorage)) + for slots := range obj.dirtyStorage { + keys = append(keys, &slots) } tuple.StorageKeys = keys acl = append(acl, tuple) diff --git a/core/vm/interface.go b/core/vm/interface.go index ad9b05d666a8..f164a402188f 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -74,6 +74,8 @@ type StateDB interface { AddPreimage(common.Hash, []byte) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error + + AccessList() *types.AccessList } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/eth/api_backend.go b/eth/api_backend.go index fbd1f28df9ff..ef8e39589e95 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -346,6 +346,9 @@ func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Blo } func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reexec uint64, tx *types.Transaction) (*types.AccessList, error) { + if block == nil { + block = b.CurrentBlock() + } statedb, release, err := b.eth.stateAtBlock(block, reexec) defer release() if err != nil { @@ -363,6 +366,6 @@ func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reex if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, fmt.Errorf("failed to apply transaction: %v", tx.Hash(), err) } - statedb.UnprepareAccessList(msg.From(), msg.To(), vmenv.ActivePrecompiles()) - return statedb.AccessList(), nil + + return vmenv.StateDB.AccessList(), nil } From b90437ced1f5e8ca4763037cf5242a36e5c017a5 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 10 Feb 2021 13:05:51 +0100 Subject: [PATCH 05/10] eth: better error --- eth/api_backend.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index ef8e39589e95..23eb1659f913 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -364,7 +364,7 @@ func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reex // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, b.eth.blockchain.Config(), vm.Config{}) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, fmt.Errorf("failed to apply transaction: %v", tx.Hash(), err) + return nil, fmt.Errorf("failed to apply transaction: %v err: %v", tx.Hash(), err) } return vmenv.StateDB.AccessList(), nil From 1c7dbfb9dbebe189a6a05390415c1f695fdbec7f Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 4 Mar 2021 12:28:34 +0100 Subject: [PATCH 06/10] rebased --- core/state/statedb.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index f2f9773871d7..abe821eeb19c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1073,10 +1073,10 @@ func (s *StateDB) CurrentAccessList() *types.AccessList { acl := make([]types.AccessTuple, 0, len(list.addresses)) for addr, idx := range list.addresses { var tuple types.AccessTuple - tuple.Address = &addr - keys := make([]*common.Hash, 0, len(list.slots[idx])) - for key, _ := range list.slots[idx] { - keys = append(keys, &key) + tuple.Address = addr + keys := make([]common.Hash, 0, len(list.slots[idx])) + for key := range list.slots[idx] { + keys = append(keys, key) } tuple.StorageKeys = keys acl = append(acl, tuple) @@ -1090,11 +1090,11 @@ func (s *StateDB) AccessList() *types.AccessList { acl := make([]types.AccessTuple, 0, len(s.journal.dirties)) for addr := range s.journal.dirties { var tuple types.AccessTuple - tuple.Address = &addr + tuple.Address = addr obj := s.stateObjects[addr] - keys := make([]*common.Hash, 0, len(obj.dirtyStorage)) + keys := make([]common.Hash, 0, len(obj.dirtyStorage)) for slots := range obj.dirtyStorage { - keys = append(keys, &slots) + keys = append(keys, slots) } tuple.StorageKeys = keys acl = append(acl, tuple) From b801cf1f7c31902f2fe3bdfdf6055456144883bc Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 4 Mar 2021 13:08:21 +0100 Subject: [PATCH 07/10] core/state: removed unnecessary functions --- core/state/journal.go | 11 ----------- core/state/statedb.go | 41 ----------------------------------------- 2 files changed, 52 deletions(-) diff --git a/core/state/journal.go b/core/state/journal.go index fe217193254e..2070f3087554 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -138,9 +138,6 @@ type ( address *common.Address slot *common.Hash } - accessListDeleteAccountChange struct { - address *common.Address - } ) func (ch createObjectChange) revert(s *StateDB) { @@ -263,14 +260,6 @@ func (ch accessListAddAccountChange) dirtied() *common.Address { return nil } -func (ch accessListDeleteAccountChange) revert(s *StateDB) { - s.accessList.AddAddress(*ch.address) -} - -func (ch accessListDeleteAccountChange) dirtied() *common.Address { - return nil -} - func (ch accessListAddSlotChange) revert(s *StateDB) { s.accessList.DeleteSlot(*ch.address, *ch.slot) } diff --git a/core/state/statedb.go b/core/state/statedb.go index abe821eeb19c..713388e8775a 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1034,27 +1034,6 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { } } -// UnprepareAccessList unprepares the access list of the statedb. -// It reverts the preparation steps from EIP-2929 -// - Delete sender from access list (2929) -// - Delete receiver from access list (2929) -// - Delete precompiles from access list (2929) -func (s *StateDB) UnprepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address) { - s.DeleteAddressFromAccessList(sender) - if dst != nil { - s.DeleteAddressFromAccessList(*dst) - } - for _, addr := range precompiles { - s.DeleteAddressFromAccessList(addr) - } -} - -// DeleteAddressFromAccessList deletes the given address from the access list -func (s *StateDB) DeleteAddressFromAccessList(addr common.Address) { - s.accessList.DeleteAddress(addr) - s.journal.append(accessListDeleteAccountChange{&addr}) -} - // AddressInAccessList returns true if the given address is in the access list. func (s *StateDB) AddressInAccessList(addr common.Address) bool { return s.accessList.ContainsAddress(addr) @@ -1065,26 +1044,6 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre return s.accessList.Contains(addr, slot) } -// CurrentAccessList returns the current access list for the StateDB. -// If the precompiles, sender and receiver are to be removed from the access list, -// a prior call to `UnprepareAccessList` is required. -func (s *StateDB) CurrentAccessList() *types.AccessList { - list := s.accessList.Copy() - acl := make([]types.AccessTuple, 0, len(list.addresses)) - for addr, idx := range list.addresses { - var tuple types.AccessTuple - tuple.Address = addr - keys := make([]common.Hash, 0, len(list.slots[idx])) - for key := range list.slots[idx] { - keys = append(keys, key) - } - tuple.StorageKeys = keys - acl = append(acl, tuple) - } - cast := types.AccessList(acl) - return &cast -} - // AccessList returns a list of dirty slots and addresses func (s *StateDB) AccessList() *types.AccessList { acl := make([]types.AccessTuple, 0, len(s.journal.dirties)) From 70ad2a922889a1b396b018570abf6daa6d0dbd16 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 14 Mar 2021 16:30:58 +0100 Subject: [PATCH 08/10] eth: added CreateAccessList functionality --- eth/api.go | 5 +++++ internal/web3ext/web3ext.go | 6 ++++++ tests/testdata | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/eth/api.go b/eth/api.go index 53ef91392ba6..14163c63dbab 100644 --- a/eth/api.go +++ b/eth/api.go @@ -543,3 +543,8 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc } return dirty, nil } + +func (api *PublicDebugAPI) CreateAccessList(block *types.Block, reexec uint64, tx *types.Transaction) (*types.AccessList, error) { + ctx := context.Background() + return api.eth.APIBackend.AccessList(ctx, block, reexec, tx) +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 6fcf4b83805d..8d9c08be0654 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -470,6 +470,12 @@ web3._extend({ call: 'debug_freezeClient', params: 1, }), + new web3._extend.Method({ + name: 'createAccessList', + call: 'debug_createAccessList', + params: 3, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, null, web3._extend.formatters.inputTransactionFormatter], + }), ], properties: [] }); diff --git a/tests/testdata b/tests/testdata index c600d7795aa2..e431795bf750 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit c600d7795aa2ea57a9c856fc79f72fc05b542124 +Subproject commit e431795bf750166671afc3516f5e0332af3318f1 From a6e36478cce440cc8ac0b5f5662758e12e845709 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 15 Mar 2021 11:56:01 +0100 Subject: [PATCH 09/10] eth: added CreateAccessList functionality --- core/state/journal.go | 11 +++++++++ core/state/statedb.go | 42 +++++++++++++++++++++++++++------- eth/api.go | 20 +++++++++++++++-- eth/api_backend.go | 45 +++++++++++++++++++++++++------------ internal/ethapi/api.go | 30 ++++++++++++------------- internal/web3ext/web3ext.go | 2 +- 6 files changed, 110 insertions(+), 40 deletions(-) diff --git a/core/state/journal.go b/core/state/journal.go index 2070f3087554..c123ea07a595 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -138,6 +138,9 @@ type ( address *common.Address slot *common.Hash } + accessListDeleteAccountChange struct { + address *common.Address + } ) func (ch createObjectChange) revert(s *StateDB) { @@ -267,3 +270,11 @@ func (ch accessListAddSlotChange) revert(s *StateDB) { func (ch accessListAddSlotChange) dirtied() *common.Address { return nil } + +func (ch accessListDeleteAccountChange) revert(s *StateDB) { + s.accessList.AddAddress(*ch.address) +} + +func (ch accessListDeleteAccountChange) dirtied() *common.Address { + return nil +} diff --git a/core/state/statedb.go b/core/state/statedb.go index 713388e8775a..43e6594a2c18 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1034,6 +1034,27 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { } } +// UnprepareAccessList unprepares an access list. +// It reverts the preparation steps from EIP-2929 +// - Delete sender from access list (2929) +// - Delete receiver from access list (2929) +// - Delete precompiles from access list (2929) +func (s *StateDB) UnprepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address) { + s.DeleteAddressFromAccessList(sender) + if dst != nil { + s.DeleteAddressFromAccessList(*dst) + } + for _, addr := range precompiles { + s.DeleteAddressFromAccessList(addr) + } +} + +// DeleteAddressFromAccessList deletes the given address from the access list +func (s *StateDB) DeleteAddressFromAccessList(addr common.Address) { + s.accessList.DeleteAddress(addr) + s.journal.append(accessListDeleteAccountChange{&addr}) +} + // AddressInAccessList returns true if the given address is in the access list. func (s *StateDB) AddressInAccessList(addr common.Address) bool { return s.accessList.ContainsAddress(addr) @@ -1044,18 +1065,23 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre return s.accessList.Contains(addr, slot) } -// AccessList returns a list of dirty slots and addresses +// AccessList returns the current access list for the StateDB. +// If the precompiles, sender and receiver are to be removed from the access list, +// a prior call to `UnprepareAccessList` is required. func (s *StateDB) AccessList() *types.AccessList { - acl := make([]types.AccessTuple, 0, len(s.journal.dirties)) - for addr := range s.journal.dirties { + list := s.accessList.Copy() + acl := make([]types.AccessTuple, 0, len(list.addresses)) + for addr, idx := range list.addresses { var tuple types.AccessTuple tuple.Address = addr - obj := s.stateObjects[addr] - keys := make([]common.Hash, 0, len(obj.dirtyStorage)) - for slots := range obj.dirtyStorage { - keys = append(keys, slots) + // addresses without slots are saved as -1 + if idx > 0 { + keys := make([]common.Hash, 0, len(list.slots[idx])) + for key := range list.slots[idx] { + keys = append(keys, key) + } + tuple.StorageKeys = keys } - tuple.StorageKeys = keys acl = append(acl, tuple) } cast := types.AccessList(acl) diff --git a/eth/api.go b/eth/api.go index 14163c63dbab..50a52dfbfb29 100644 --- a/eth/api.go +++ b/eth/api.go @@ -544,7 +544,23 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc return dirty, nil } -func (api *PublicDebugAPI) CreateAccessList(block *types.Block, reexec uint64, tx *types.Transaction) (*types.AccessList, error) { +func (api *PublicDebugAPI) CreateAccessList(blockNrOrHash rpc.BlockNumberOrHash, reexec uint64, args *ethapi.SendTxArgs) (*types.AccessList, error) { ctx := context.Background() - return api.eth.APIBackend.AccessList(ctx, block, reexec, tx) + var block *types.Block + if number, ok := blockNrOrHash.Number(); ok { + if number == rpc.PendingBlockNumber { + block, _ = api.eth.miner.Pending() + } else { + if number == rpc.LatestBlockNumber { + block = api.eth.blockchain.CurrentBlock() + } else { + block = api.eth.blockchain.GetBlockByNumber(uint64(number)) + } + } + } else if hash, ok := blockNrOrHash.Hash(); ok { + block = api.eth.blockchain.GetBlockByHash(hash) + } else { + return nil, errors.New("either block number or block hash must be specified") + } + return api.eth.APIBackend.AccessList(ctx, block, reexec, args) } diff --git a/eth/api_backend.go b/eth/api_backend.go index 23eb1659f913..cd534b5f6bcd 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -345,27 +346,43 @@ func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Blo return b.eth.stateAtTransaction(block, txIndex, reexec) } -func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reexec uint64, tx *types.Transaction) (*types.AccessList, error) { +func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reexec uint64, args *ethapi.SendTxArgs) (*types.AccessList, error) { if block == nil { block = b.CurrentBlock() } - statedb, release, err := b.eth.stateAtBlock(block, reexec) + db, release, err := b.eth.stateAtBlock(block, reexec) defer release() if err != nil { return nil, err } - signer := types.MakeSigner(b.eth.blockchain.Config(), block.Number()) - msg, err := tx.AsMessage(signer) - if err != nil { - return nil, err - } - txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), b.eth.blockchain, nil) - // Not yet the searched for transaction, execute on top of the current state - vmenv := vm.NewEVM(context, txContext, statedb, b.eth.blockchain.Config(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, fmt.Errorf("failed to apply transaction: %v err: %v", tx.Hash(), err) + args.SetDefaults(ctx, b) + var ( + gas uint64 + accessList = args.AccessList + msg types.Message + ) + for i := 0; i < 10; i++ { + // Copy the original db so we don't modify it + statedb := db.Copy() + // If we have an accesslist, use it + if accessList != nil { + msg = types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), *args.Data, *accessList, false) + } else { + msg = types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), *args.Data, nil, false) + } + // Apply the transaction + context := core.NewEVMBlockContext(block.Header(), b.eth.blockchain, nil) + vmenv := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, b.eth.blockchain.Config(), vm.Config{}) + res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) + if err != nil { + return nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction().Hash(), err) + } + if res.UsedGas == gas { + return vmenv.StateDB.AccessList(), nil + } + accessList = vmenv.StateDB.AccessList() + gas = res.UsedGas } - return vmenv.StateDB.AccessList(), nil + return accessList, errors.New("failed to create accesslist") } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 622063cf6497..47349f58bef8 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -358,11 +358,11 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *SendTxArg return nil, err } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.SetDefaults(ctx, s.b); err != nil { return nil, err } // Assemble the transaction and sign with the wallet - tx := args.toTransaction() + tx := args.ToTransaction() return wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID) } @@ -1501,8 +1501,8 @@ type SendTxArgs struct { ChainID *hexutil.Big `json:"chainId,omitempty"` } -// setDefaults fills in default values for unspecified tx fields. -func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { +// SetDefaults fills in default values for unspecified tx fields. +func (args *SendTxArgs) SetDefaults(ctx context.Context, b Backend) error { if args.GasPrice == nil { price, err := b.SuggestPrice(ctx) if err != nil { @@ -1567,9 +1567,9 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { return nil } -// toTransaction converts the arguments to a transaction. +// ToTransaction converts the arguments to a transaction. // This assumes that setDefaults has been called. -func (args *SendTxArgs) toTransaction() *types.Transaction { +func (args *SendTxArgs) ToTransaction() *types.Transaction { var input []byte if args.Input != nil { input = *args.Input @@ -1651,11 +1651,11 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.SetDefaults(ctx, s.b); err != nil { return common.Hash{}, err } // Assemble the transaction and sign with the wallet - tx := args.toTransaction() + tx := args.ToTransaction() signed, err := wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) if err != nil { @@ -1668,11 +1668,11 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen // and returns it to the caller for further processing (signing + broadcast) func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) { // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.SetDefaults(ctx, s.b); err != nil { return nil, err } // Assemble the transaction and obtain rlp - tx := args.toTransaction() + tx := args.ToTransaction() data, err := tx.MarshalBinary() if err != nil { return nil, err @@ -1734,14 +1734,14 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen if args.Nonce == nil { return nil, fmt.Errorf("nonce not specified") } - if err := args.setDefaults(ctx, s.b); err != nil { + if err := args.SetDefaults(ctx, s.b); err != nil { return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil { return nil, err } - tx, err := s.sign(args.From, args.toTransaction()) + tx, err := s.sign(args.From, args.ToTransaction()) if err != nil { return nil, err } @@ -1781,10 +1781,10 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr if sendArgs.Nonce == nil { return common.Hash{}, fmt.Errorf("missing transaction nonce in transaction spec") } - if err := sendArgs.setDefaults(ctx, s.b); err != nil { + if err := sendArgs.SetDefaults(ctx, s.b); err != nil { return common.Hash{}, err } - matchTx := sendArgs.toTransaction() + matchTx := sendArgs.ToTransaction() // Before replacing the old transaction, ensure the _new_ transaction fee is reasonable. var price = matchTx.GasPrice() @@ -1814,7 +1814,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr if gasLimit != nil && *gasLimit != 0 { sendArgs.Gas = gasLimit } - signedTx, err := s.sign(sendArgs.From, sendArgs.toTransaction()) + signedTx, err := s.sign(sendArgs.From, sendArgs.ToTransaction()) if err != nil { return common.Hash{}, err } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 8d9c08be0654..924236542fac 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -474,7 +474,7 @@ web3._extend({ name: 'createAccessList', call: 'debug_createAccessList', params: 3, - inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, null, web3._extend.formatters.inputTransactionFormatter], + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, null, null], }), ], properties: [] From e46bd5e7e845aaf5a38cc4ccea159319ea447b92 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 15 Mar 2021 12:39:41 +0100 Subject: [PATCH 10/10] core: added unprepareAccessList --- core/vm/interface.go | 1 + eth/api_backend.go | 1 + 2 files changed, 2 insertions(+) diff --git a/core/vm/interface.go b/core/vm/interface.go index f164a402188f..1d68963bcdbf 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -58,6 +58,7 @@ type StateDB interface { Empty(common.Address) bool PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) + UnprepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address) AddressInAccessList(addr common.Address) bool SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform diff --git a/eth/api_backend.go b/eth/api_backend.go index cd534b5f6bcd..3816a5d9ac15 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -378,6 +378,7 @@ func (b *EthAPIBackend) AccessList(ctx context.Context, block *types.Block, reex return nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction().Hash(), err) } if res.UsedGas == gas { + vmenv.StateDB.UnprepareAccessList(args.From, args.To, vmenv.ActivePrecompiles()) return vmenv.StateDB.AccessList(), nil } accessList = vmenv.StateDB.AccessList()