forked from cerc-io/plugeth
core, eth, internal/ethapi: create access list RPC API (#22550)
* core/vm: implement AccessListTracer * eth: implement debug.createAccessList * core/vm: fixed nil panics in accessListTracer * eth: better error messages for createAccessList * eth: some fixes on CreateAccessList * eth: allow for provided accesslists * eth: pass accesslist by value * eth: remove created acocunt from accesslist * core/vm: simplify access list tracer * core/vm: unexport accessListTracer * eth: return best guess if al iteration times out * eth: return best guess if al iteration times out * core: docstring, unexport methods * eth: typo * internal/ethapi: move createAccessList to eth package * internal/ethapi: remove reexec from createAccessList * internal/ethapi: break if al is equal to last run, not if gas is equal * internal/web3ext: fixed arguments * core/types: fixed equality check for accesslist * core/types: no hardcoded vals * core, internal: simplify access list generation, make it precise * core/vm: fix typo Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Péter Szilágyi <peterke@gmail.com>
This commit is contained in:
parent
a600dab7e5
commit
9d10856e84
@ -259,10 +259,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up the initial access list.
|
// Set up the initial access list.
|
||||||
if st.evm.ChainConfig().IsBerlin(st.evm.Context.BlockNumber) {
|
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
|
||||||
st.state.PrepareAccessList(msg.From(), msg.To(), st.evm.ActivePrecompiles(), msg.AccessList())
|
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ret []byte
|
ret []byte
|
||||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
|
177
core/vm/access_list_tracer.go
Normal file
177
core/vm/access_list_tracer.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Copyright 2021 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// accessList is an accumulator for the set of accounts and storage slots an EVM
|
||||||
|
// contract execution touches.
|
||||||
|
type accessList map[common.Address]accessListSlots
|
||||||
|
|
||||||
|
// accessListSlots is an accumulator for the set of storage slots within a single
|
||||||
|
// contract that an EVM contract execution touches.
|
||||||
|
type accessListSlots map[common.Hash]struct{}
|
||||||
|
|
||||||
|
// newAccessList creates a new accessList.
|
||||||
|
func newAccessList() accessList {
|
||||||
|
return make(map[common.Address]accessListSlots)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addAddress adds an address to the accesslist.
|
||||||
|
func (al accessList) addAddress(address common.Address) {
|
||||||
|
// Set address if not previously present
|
||||||
|
if _, present := al[address]; !present {
|
||||||
|
al[address] = make(map[common.Hash]struct{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSlot adds a storage slot to the accesslist.
|
||||||
|
func (al accessList) addSlot(address common.Address, slot common.Hash) {
|
||||||
|
// Set address if not previously present
|
||||||
|
al.addAddress(address)
|
||||||
|
|
||||||
|
// Set the slot on the surely existent storage set
|
||||||
|
al[address][slot] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// equal checks if the content of the current access list is the same as the
|
||||||
|
// content of the other one.
|
||||||
|
func (al accessList) equal(other accessList) bool {
|
||||||
|
// Cross reference the accounts first
|
||||||
|
if len(al) != len(other) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for addr := range al {
|
||||||
|
if _, ok := other[addr]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for addr := range other {
|
||||||
|
if _, ok := al[addr]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Accounts match, cross reference the storage slots too
|
||||||
|
for addr, slots := range al {
|
||||||
|
otherslots := other[addr]
|
||||||
|
|
||||||
|
if len(slots) != len(otherslots) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for hash := range slots {
|
||||||
|
if _, ok := otherslots[hash]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for hash := range otherslots {
|
||||||
|
if _, ok := slots[hash]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// accesslist converts the accesslist to a types.AccessList.
|
||||||
|
func (al accessList) accessList() types.AccessList {
|
||||||
|
acl := make(types.AccessList, 0, len(al))
|
||||||
|
for addr, slots := range al {
|
||||||
|
tuple := types.AccessTuple{Address: addr}
|
||||||
|
for slot := range slots {
|
||||||
|
tuple.StorageKeys = append(tuple.StorageKeys, slot)
|
||||||
|
}
|
||||||
|
acl = append(acl, tuple)
|
||||||
|
}
|
||||||
|
return acl
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessListTracer is a tracer that accumulates touched accounts and storage
|
||||||
|
// slots into an internal set.
|
||||||
|
type AccessListTracer struct {
|
||||||
|
excl map[common.Address]struct{} // Set of account to exclude from the list
|
||||||
|
list accessList // Set of accounts and storage slots touched
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAccessListTracer creates a new tracer that can generate AccessLists.
|
||||||
|
// An optional AccessList can be specified to occupy slots and addresses in
|
||||||
|
// the resulting accesslist.
|
||||||
|
func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
|
||||||
|
excl := map[common.Address]struct{}{
|
||||||
|
from: {}, to: {},
|
||||||
|
}
|
||||||
|
for _, addr := range precompiles {
|
||||||
|
excl[addr] = struct{}{}
|
||||||
|
}
|
||||||
|
list := newAccessList()
|
||||||
|
for _, al := range acl {
|
||||||
|
if _, ok := excl[al.Address]; !ok {
|
||||||
|
list.addAddress(al.Address)
|
||||||
|
}
|
||||||
|
for _, slot := range al.StorageKeys {
|
||||||
|
list.addSlot(al.Address, slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &AccessListTracer{
|
||||||
|
excl: excl,
|
||||||
|
list: list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||||
|
func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||||
|
stack := scope.Stack
|
||||||
|
if (op == SLOAD || op == SSTORE) && stack.len() >= 1 {
|
||||||
|
slot := common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||||
|
a.list.addSlot(scope.Contract.Address(), slot)
|
||||||
|
}
|
||||||
|
if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.len() >= 1 {
|
||||||
|
addr := common.Address(stack.data[stack.len()-1].Bytes20())
|
||||||
|
if _, ok := a.excl[addr]; !ok {
|
||||||
|
a.list.addAddress(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.len() >= 5 {
|
||||||
|
addr := common.Address(stack.data[stack.len()-2].Bytes20())
|
||||||
|
if _, ok := a.excl[addr]; !ok {
|
||||||
|
a.list.addAddress(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
|
||||||
|
|
||||||
|
// AccessList returns the current accesslist maintained by the tracer.
|
||||||
|
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||||
|
return a.list.accessList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns if the content of two access list traces are equal.
|
||||||
|
func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
|
||||||
|
return a.list.equal(other.list)
|
||||||
|
}
|
@ -128,6 +128,20 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActivePrecompiles returns the precompiles enabled with the current configuration.
|
||||||
|
func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||||
|
switch {
|
||||||
|
case rules.IsBerlin:
|
||||||
|
return PrecompiledAddressesBerlin
|
||||||
|
case rules.IsIstanbul:
|
||||||
|
return PrecompiledAddressesIstanbul
|
||||||
|
case rules.IsByzantium:
|
||||||
|
return PrecompiledAddressesByzantium
|
||||||
|
default:
|
||||||
|
return PrecompiledAddressesHomestead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
|
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
|
||||||
// It returns
|
// It returns
|
||||||
// - the returned bytes,
|
// - the returned bytes,
|
||||||
|
@ -42,21 +42,6 @@ type (
|
|||||||
GetHashFunc func(uint64) common.Hash
|
GetHashFunc func(uint64) common.Hash
|
||||||
)
|
)
|
||||||
|
|
||||||
// ActivePrecompiles returns the addresses of the precompiles enabled with the current
|
|
||||||
// configuration
|
|
||||||
func (evm *EVM) ActivePrecompiles() []common.Address {
|
|
||||||
switch {
|
|
||||||
case evm.chainRules.IsBerlin:
|
|
||||||
return PrecompiledAddressesBerlin
|
|
||||||
case evm.chainRules.IsIstanbul:
|
|
||||||
return PrecompiledAddressesIstanbul
|
|
||||||
case evm.chainRules.IsByzantium:
|
|
||||||
return PrecompiledAddressesByzantium
|
|
||||||
default:
|
|
||||||
return PrecompiledAddressesHomestead
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
||||||
var precompiles map[common.Address]PrecompiledContract
|
var precompiles map[common.Address]PrecompiledContract
|
||||||
switch {
|
switch {
|
||||||
|
@ -114,8 +114,8 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||||||
vmenv = NewEnv(cfg)
|
vmenv = NewEnv(cfg)
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
sender = vm.AccountRef(cfg.Origin)
|
||||||
)
|
)
|
||||||
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
|
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
|
||||||
cfg.State.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
|
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||||
}
|
}
|
||||||
cfg.State.CreateAccount(address)
|
cfg.State.CreateAccount(address)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
@ -146,10 +146,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||||||
vmenv = NewEnv(cfg)
|
vmenv = NewEnv(cfg)
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
sender = vm.AccountRef(cfg.Origin)
|
||||||
)
|
)
|
||||||
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
|
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
|
||||||
cfg.State.PrepareAccessList(cfg.Origin, nil, vmenv.ActivePrecompiles(), nil)
|
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
code, address, leftOverGas, err := vmenv.Create(
|
code, address, leftOverGas, err := vmenv.Create(
|
||||||
sender,
|
sender,
|
||||||
@ -172,10 +171,10 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||||||
|
|
||||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||||
statedb := cfg.State
|
statedb := cfg.State
|
||||||
if cfg.ChainConfig.IsBerlin(vmenv.Context.BlockNumber) {
|
|
||||||
statedb.PrepareAccessList(cfg.Origin, &address, vmenv.ActivePrecompiles(), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin {
|
||||||
|
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||||
|
}
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
ret, leftOverGas, err := vmenv.Call(
|
ret, leftOverGas, err := vmenv.Call(
|
||||||
sender,
|
sender,
|
||||||
@ -184,6 +183,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||||||
cfg.GasLimit,
|
cfg.GasLimit,
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
)
|
)
|
||||||
|
|
||||||
return ret, leftOverGas, err
|
return ret, leftOverGas, err
|
||||||
}
|
}
|
||||||
|
@ -192,12 +192,14 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
|||||||
return b.eth.blockchain.GetTdByHash(hash)
|
return b.eth.blockchain.GetTdByHash(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
|
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
|
||||||
vmError := func() error { return nil }
|
vmError := func() error { return nil }
|
||||||
|
if vmConfig == nil {
|
||||||
|
vmConfig = b.eth.blockchain.GetVMConfig()
|
||||||
|
}
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
|
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
|
||||||
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *b.eth.blockchain.GetVMConfig()), vmError, nil
|
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), vmError, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
|
@ -865,7 +865,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
|
|||||||
|
|
||||||
// Get a new instance of the EVM.
|
// Get a new instance of the EVM.
|
||||||
msg := args.ToMessage(globalGasCap)
|
msg := args.ToMessage(globalGasCap)
|
||||||
evm, vmError, err := b.GetEVM(ctx, msg, state, header)
|
evm, vmError, err := b.GetEVM(ctx, msg, state, header, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1303,6 +1303,106 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accessListResult returns an optional accesslist
|
||||||
|
// Its the result of the `debug_createAccessList` RPC call.
|
||||||
|
// It contains an error if the transaction itself failed.
|
||||||
|
type accessListResult struct {
|
||||||
|
Accesslist *types.AccessList `json:"accessList"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAccessList creates a EIP-2930 type AccessList for the given transaction.
|
||||||
|
// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state.
|
||||||
|
func (s *PublicBlockChainAPI) CreateAccessList(ctx context.Context, args SendTxArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) {
|
||||||
|
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
|
if blockNrOrHash != nil {
|
||||||
|
bNrOrHash = *blockNrOrHash
|
||||||
|
}
|
||||||
|
acl, gasUsed, vmerr, err := AccessList(ctx, s.b, bNrOrHash, args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := &accessListResult{Accesslist: &acl, GasUsed: hexutil.Uint64(gasUsed)}
|
||||||
|
if vmerr != nil {
|
||||||
|
result.Error = vmerr.Error()
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessList creates an access list for the given transaction.
|
||||||
|
// If the accesslist creation fails an error is returned.
|
||||||
|
// If the transaction itself fails, an vmErr is returned.
|
||||||
|
func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrHash, args SendTxArgs) (acl types.AccessList, gasUsed uint64, vmErr error, err error) {
|
||||||
|
// Retrieve the execution context
|
||||||
|
db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
|
if db == nil || err != nil {
|
||||||
|
return nil, 0, nil, err
|
||||||
|
}
|
||||||
|
// If the gas amount is not set, extract this as it will depend on access
|
||||||
|
// lists and we'll need to reestimate every time
|
||||||
|
nogas := args.Gas == nil
|
||||||
|
|
||||||
|
// Ensure any missing fields are filled, extract the recipient and input data
|
||||||
|
if err := args.setDefaults(ctx, b); err != nil {
|
||||||
|
return nil, 0, nil, err
|
||||||
|
}
|
||||||
|
var to common.Address
|
||||||
|
if args.To != nil {
|
||||||
|
to = *args.To
|
||||||
|
} else {
|
||||||
|
to = crypto.CreateAddress(args.From, uint64(*args.Nonce))
|
||||||
|
}
|
||||||
|
var input []byte
|
||||||
|
if args.Input != nil {
|
||||||
|
input = *args.Input
|
||||||
|
} else if args.Data != nil {
|
||||||
|
input = *args.Data
|
||||||
|
}
|
||||||
|
// Retrieve the precompiles since they don't need to be added to the access list
|
||||||
|
precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number))
|
||||||
|
|
||||||
|
// Create an initial tracer
|
||||||
|
prevTracer := vm.NewAccessListTracer(nil, args.From, to, precompiles)
|
||||||
|
if args.AccessList != nil {
|
||||||
|
prevTracer = vm.NewAccessListTracer(*args.AccessList, args.From, to, precompiles)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
// Retrieve the current access list to expand
|
||||||
|
accessList := prevTracer.AccessList()
|
||||||
|
log.Trace("Creating access list", "input", accessList)
|
||||||
|
|
||||||
|
// If no gas amount was specified, each unique access list needs it's own
|
||||||
|
// gas calculation. This is quite expensive, but we need to be accurate
|
||||||
|
// and it's convered by the sender only anyway.
|
||||||
|
if nogas {
|
||||||
|
args.Gas = nil
|
||||||
|
if err := args.setDefaults(ctx, b); err != nil {
|
||||||
|
return nil, 0, nil, err // shouldn't happen, just in case
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy the original db so we don't modify it
|
||||||
|
statedb := db.Copy()
|
||||||
|
msg := types.NewMessage(args.From, args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), input, accessList, false)
|
||||||
|
|
||||||
|
// Apply the transaction with the access list tracer
|
||||||
|
tracer := vm.NewAccessListTracer(accessList, args.From, to, precompiles)
|
||||||
|
config := vm.Config{Tracer: tracer, Debug: true}
|
||||||
|
vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil, err
|
||||||
|
}
|
||||||
|
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
||||||
|
}
|
||||||
|
if tracer.Equal(prevTracer) {
|
||||||
|
return accessList, res.UsedGas, res.Err, nil
|
||||||
|
}
|
||||||
|
prevTracer = tracer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PublicTransactionPoolAPI exposes methods for the RPC interface
|
// PublicTransactionPoolAPI exposes methods for the RPC interface
|
||||||
type PublicTransactionPoolAPI struct {
|
type PublicTransactionPoolAPI struct {
|
||||||
b Backend
|
b Backend
|
||||||
@ -1539,7 +1639,6 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
|||||||
return errors.New(`contract creation without any data provided`)
|
return errors.New(`contract creation without any data provided`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estimate the gas usage if necessary.
|
// Estimate the gas usage if necessary.
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
// For backwards-compatibility reason, we try both input and data
|
// For backwards-compatibility reason, we try both input and data
|
||||||
@ -1580,7 +1679,6 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
|
|||||||
} else if args.Data != nil {
|
} else if args.Data != nil {
|
||||||
input = *args.Data
|
input = *args.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
var data types.TxData
|
var data types.TxData
|
||||||
if args.AccessList == nil {
|
if args.AccessList == nil {
|
||||||
data = &types.LegacyTx{
|
data = &types.LegacyTx{
|
||||||
|
@ -63,7 +63,7 @@ type Backend interface {
|
|||||||
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
||||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||||
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
||||||
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error)
|
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error)
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||||
|
@ -575,6 +575,12 @@ web3._extend({
|
|||||||
params: 3,
|
params: 3,
|
||||||
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter]
|
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter]
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'createAccessList',
|
||||||
|
call: 'eth_createAccessList',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
properties: [
|
properties: [
|
||||||
new web3._extend.Property({
|
new web3._extend.Property({
|
||||||
|
@ -171,10 +171,13 @@ func (b *LesApiBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
|
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
|
||||||
|
if vmConfig == nil {
|
||||||
|
vmConfig = new(vm.Config)
|
||||||
|
}
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
|
context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
|
||||||
return vm.NewEVM(context, txContext, state, b.eth.chainConfig, vm.Config{}), state.Error, nil
|
return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user