mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
add transient override param
This commit is contained in:
parent
277c2e5620
commit
8c696939f2
7 changed files with 79 additions and 55 deletions
|
|
@ -171,6 +171,31 @@ func (ec *Client) CallContractWithBlockOverrides(ctx context.Context, msg ethere
|
|||
return hex, err
|
||||
}
|
||||
|
||||
// CallContractWithTransientOverrides executes a message call transaction, which is directly executed
|
||||
// in the VM of the node, but never mined into the blockchain.
|
||||
//
|
||||
// blockNumber selects the block height at which the call runs. It can be nil, in which
|
||||
// case the code is taken from the latest known block. Note that state from very old
|
||||
// blocks might not be available.
|
||||
//
|
||||
// overrides specifies a map of contract states that should be overwritten before executing
|
||||
// the message call.
|
||||
//
|
||||
// blockOverrides specifies block fields exposed to the EVM that can be overridden for the call.
|
||||
//
|
||||
// transientOverrides specifies transient storage slots that should be overwritten before executing
|
||||
// the message call. Transient storage is reset at the beginning of each transaction.
|
||||
//
|
||||
// Please use ethclient.CallContract instead if you don't need the override functionality.
|
||||
func (ec *Client) CallContractWithTransientOverrides(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount, blockOverrides *BlockOverrides, transientOverrides *TransientOverrides) ([]byte, error) {
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(
|
||||
ctx, &hex, "eth_call", toCallArg(msg),
|
||||
toBlockNumArg(blockNumber), overrides, blockOverrides, transientOverrides,
|
||||
)
|
||||
return hex, err
|
||||
}
|
||||
|
||||
// GCStats retrieves the current garbage collection stats from a geth node.
|
||||
func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
|
||||
var result debug.GCStats
|
||||
|
|
@ -407,3 +432,7 @@ type OverrideAccount = ethereum.OverrideAccount
|
|||
|
||||
// BlockOverrides is an alias for ethereum.BlockOverrides.
|
||||
type BlockOverrides = ethereum.BlockOverrides
|
||||
|
||||
// TransientOverrides specifies transient storage slots to override for eth_call.
|
||||
// The map key is the contract address, and the value is another map of slot to value.
|
||||
type TransientOverrides map[common.Address]map[common.Hash]common.Hash
|
||||
|
|
|
|||
|
|
@ -1207,7 +1207,7 @@ func (c *CallResult) Status() hexutil.Uint64 {
|
|||
func (b *Block) Call(ctx context.Context, args struct {
|
||||
Data ethapi.TransactionArgs
|
||||
}) (*CallResult, error) {
|
||||
result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
|
||||
result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1270,7 +1270,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
|||
Data ethapi.TransactionArgs
|
||||
}) (*CallResult, error) {
|
||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||
result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
|
||||
result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, nil, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,26 +317,21 @@ type OverrideAccount struct {
|
|||
|
||||
// StateDiff allows overriding individual storage slots.
|
||||
StateDiff map[common.Hash]common.Hash
|
||||
|
||||
// TransientStorage allows overriding transient storage slots.
|
||||
TransientStorage map[common.Hash]common.Hash
|
||||
}
|
||||
|
||||
func (a OverrideAccount) MarshalJSON() ([]byte, error) {
|
||||
type acc struct {
|
||||
Nonce hexutil.Uint64 `json:"nonce,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
Balance *hexutil.Big `json:"balance,omitempty"`
|
||||
State interface{} `json:"state,omitempty"`
|
||||
StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"`
|
||||
TransientStorage map[common.Hash]common.Hash `json:"transientStorage,omitempty"`
|
||||
Nonce hexutil.Uint64 `json:"nonce,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
Balance *hexutil.Big `json:"balance,omitempty"`
|
||||
State interface{} `json:"state,omitempty"`
|
||||
StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"`
|
||||
}
|
||||
|
||||
output := acc{
|
||||
Nonce: hexutil.Uint64(a.Nonce),
|
||||
Balance: (*hexutil.Big)(a.Balance),
|
||||
StateDiff: a.StateDiff,
|
||||
TransientStorage: a.TransientStorage,
|
||||
Nonce: hexutil.Uint64(a.Nonce),
|
||||
Balance: (*hexutil.Big)(a.Balance),
|
||||
StateDiff: a.StateDiff,
|
||||
}
|
||||
if a.Code != nil {
|
||||
output.Code = hexutil.Encode(a.Code)
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ func (context *ChainContext) GetHeaderByHash(hash common.Hash) *types.Header {
|
|||
return header
|
||||
}
|
||||
|
||||
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, transientOverrides *override.TransientStorageOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
|
||||
if blockOverrides != nil {
|
||||
if err := blockOverrides.Apply(&blockCtx); err != nil {
|
||||
|
|
@ -740,6 +740,9 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Apply transient storage overrides. These will be applied after Prepare() is called.
|
||||
transientOverrides.Apply(state)
|
||||
|
||||
// Setup context so it may be cancelled the call has completed
|
||||
// or, in case of unmetered gas, setup a context with a timeout.
|
||||
var cancel context.CancelFunc
|
||||
|
|
@ -807,14 +810,14 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, ti
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, transientOverrides *override.TransientStorageOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||
|
||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if state == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return doCall(ctx, b, args, state, header, overrides, blockOverrides, timeout, globalGasCap)
|
||||
return doCall(ctx, b, args, state, header, overrides, blockOverrides, transientOverrides, timeout, globalGasCap)
|
||||
}
|
||||
|
||||
// Call executes the given transaction on the state for the given block number.
|
||||
|
|
@ -823,12 +826,12 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
|||
//
|
||||
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||
// useful to execute and retrieve values.
|
||||
func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides) (hexutil.Bytes, error) {
|
||||
func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, transientOverrides *override.TransientStorageOverride) (hexutil.Bytes, error) {
|
||||
if blockNrOrHash == nil {
|
||||
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||
blockNrOrHash = &latest
|
||||
}
|
||||
result, err := DoCall(ctx, api.b, args, *blockNrOrHash, overrides, blockOverrides, api.b.RPCEVMTimeout(), api.b.RPCGasCap())
|
||||
result, err := DoCall(ctx, api.b, args, *blockNrOrHash, overrides, blockOverrides, transientOverrides, api.b.RPCEVMTimeout(), api.b.RPCGasCap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1032,13 +1032,14 @@ func TestCall(t *testing.T) {
|
|||
}))
|
||||
randomAccounts := newAccounts(3)
|
||||
var testSuite = []struct {
|
||||
name string
|
||||
blockNumber rpc.BlockNumber
|
||||
overrides override.StateOverride
|
||||
call TransactionArgs
|
||||
blockOverrides override.BlockOverrides
|
||||
expectErr error
|
||||
want string
|
||||
name string
|
||||
blockNumber rpc.BlockNumber
|
||||
overrides override.StateOverride
|
||||
call TransactionArgs
|
||||
blockOverrides override.BlockOverrides
|
||||
transientOverrides override.TransientStorageOverride
|
||||
expectErr error
|
||||
want string
|
||||
}{
|
||||
// transfer on genesis
|
||||
{
|
||||
|
|
@ -1303,16 +1304,18 @@ func TestCall(t *testing.T) {
|
|||
randomAccounts[2].addr: override.OverrideAccount{
|
||||
// PUSH1 0x00 TLOAD PUSH1 0x00 MSTORE PUSH1 0x20 PUSH1 0x00 RETURN
|
||||
Code: hex2Bytes("0x60005c60005260206000f3"),
|
||||
TransientStorage: map[common.Hash]common.Hash{
|
||||
common.Hash{}: common.HexToHash("0xabcd"),
|
||||
},
|
||||
},
|
||||
},
|
||||
transientOverrides: override.TransientStorageOverride{
|
||||
randomAccounts[2].addr: map[common.Hash]common.Hash{
|
||||
common.Hash{}: common.HexToHash("0xabcd"),
|
||||
},
|
||||
},
|
||||
want: "0x000000000000000000000000000000000000000000000000000000000000abcd",
|
||||
},
|
||||
}
|
||||
for _, tc := range testSuite {
|
||||
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
|
||||
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides, &tc.transientOverrides)
|
||||
if tc.expectErr != nil {
|
||||
if err == nil {
|
||||
t.Errorf("test %s: want error %v, have nothing", tc.name, tc.expectErr)
|
||||
|
|
|
|||
|
|
@ -44,13 +44,15 @@ type OverrideAccount struct {
|
|||
Balance *hexutil.Big `json:"balance"`
|
||||
State map[common.Hash]common.Hash `json:"state"`
|
||||
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
TransientStorage map[common.Hash]common.Hash `json:"transientStorage"`
|
||||
MovePrecompileTo *common.Address `json:"movePrecompileToAddress"`
|
||||
}
|
||||
|
||||
// StateOverride is the collection of overridden accounts.
|
||||
type StateOverride map[common.Address]OverrideAccount
|
||||
|
||||
// TransientStorageOverride is the collection of transient storage overrides.
|
||||
type TransientStorageOverride map[common.Address]map[common.Hash]common.Hash
|
||||
|
||||
func (diff *StateOverride) has(address common.Address) bool {
|
||||
_, ok := (*diff)[address]
|
||||
return ok
|
||||
|
|
@ -61,22 +63,10 @@ func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.Precompi
|
|||
if diff == nil {
|
||||
return nil
|
||||
}
|
||||
// Get transient storage overrides to apply after Prepare()
|
||||
transientOverrides := make(map[common.Address]map[common.Hash]common.Hash)
|
||||
for addr, account := range *diff {
|
||||
if account.TransientStorage != nil && len(account.TransientStorage) > 0 {
|
||||
transientOverrides[addr] = account.TransientStorage
|
||||
}
|
||||
}
|
||||
|
||||
// Store transient storage overrides
|
||||
if len(transientOverrides) > 0 {
|
||||
statedb.SetPendingTransientOverrides(transientOverrides)
|
||||
}
|
||||
|
||||
// Iterate in deterministic order so error messages and behavior are stable (e.g. for tests).
|
||||
addrs := slices.SortedFunc(maps.Keys(*diff), common.Address.Cmp)
|
||||
|
||||
|
||||
// Tracks destinations of precompiles that were moved.
|
||||
dirtyAddrs := make(map[common.Address]struct{})
|
||||
for _, addr := range addrs {
|
||||
|
|
@ -139,6 +129,14 @@ func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.Precompi
|
|||
return nil
|
||||
}
|
||||
|
||||
// Apply stores transient storage overrides to be applied after Prepare().
|
||||
func (diff *TransientStorageOverride) Apply(statedb *state.StateDB) {
|
||||
if diff == nil || len(*diff) == 0 {
|
||||
return
|
||||
}
|
||||
statedb.SetPendingTransientOverrides(*diff)
|
||||
}
|
||||
|
||||
// BlockOverrides is a set of header fields to override.
|
||||
type BlockOverrides struct {
|
||||
Number *hexutil.Big
|
||||
|
|
|
|||
|
|
@ -151,19 +151,15 @@ func TestStateOverrideTransientStorage(t *testing.T) {
|
|||
t.Fatalf("expected initial transient state to be empty, got %s", got.Hex())
|
||||
}
|
||||
|
||||
// Apply override with transient storage
|
||||
override := StateOverride{
|
||||
addr: OverrideAccount{
|
||||
TransientStorage: map[common.Hash]common.Hash{
|
||||
key1: value1,
|
||||
key2: value2,
|
||||
},
|
||||
// Apply transient storage override
|
||||
transientOverride := TransientStorageOverride{
|
||||
addr: map[common.Hash]common.Hash{
|
||||
key1: value1,
|
||||
key2: value2,
|
||||
},
|
||||
}
|
||||
|
||||
if err := override.Apply(statedb, nil); err != nil {
|
||||
t.Fatalf("failed to apply override: %v", err)
|
||||
}
|
||||
transientOverride.Apply(statedb)
|
||||
|
||||
statedb.Prepare(params.Rules{}, common.Address{}, common.Address{}, nil, nil, nil)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue