Skip to content

Commit b6f3caa

Browse files
committed
internal/ethapi: add block overrides to eth_call ethereum#26414
1 parent 7140141 commit b6f3caa

File tree

9 files changed

+291
-46
lines changed

9 files changed

+291
-46
lines changed

eth/api_backend.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,19 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
254254
return b.eth.blockchain.GetTdByHash(hash)
255255
}
256256

257-
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
257+
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) {
258258
vmError := func() error { return nil }
259259
if vmConfig == nil {
260260
vmConfig = b.eth.blockchain.GetVMConfig()
261261
}
262262
state.SetBalance(msg.From, math.MaxBig256)
263263
txContext := core.NewEVMTxContext(msg)
264-
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
264+
var context vm.BlockContext
265+
if blockCtx != nil {
266+
context = *blockCtx
267+
} else {
268+
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
269+
}
265270
return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil
266271
}
267272

eth/tracers/api.go

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -101,34 +101,10 @@ func NewAPI(backend Backend) *API {
101101
return &API{backend: backend}
102102
}
103103

104-
type chainContext struct {
105-
api *API
106-
ctx context.Context
107-
}
108-
109-
func (context *chainContext) Engine() consensus.Engine {
110-
return context.api.backend.Engine()
111-
}
112-
113-
func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
114-
header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
115-
if err != nil {
116-
return nil
117-
}
118-
if header.Hash() == hash {
119-
return header
120-
}
121-
header, err = context.api.backend.HeaderByHash(context.ctx, hash)
122-
if err != nil {
123-
return nil
124-
}
125-
return header
126-
}
127-
128104
// chainContext construts the context reader which is used by the evm for reading
129105
// the necessary chain context.
130106
func (api *API) chainContext(ctx context.Context) core.ChainContext {
131-
return &chainContext{api: api, ctx: ctx}
107+
return ethapi.NewChainContext(ctx, api.backend)
132108
}
133109

134110
// blockByNumber is the wrapper of the chain access function offered by the backend.

ethclient/ethclient.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ func NewClient(c *rpc.Client) *Client {
5353

5454
// Blockchain Access
5555

56+
// ChainID retrieves the current chain ID for transaction replay protection.
57+
func (ec *Client) ChainID(ctx context.Context) (*big.Int, error) {
58+
var result hexutil.Big
59+
err := ec.c.CallContext(ctx, &result, "eth_chainId")
60+
if err != nil {
61+
return nil, err
62+
}
63+
return (*big.Int)(&result), err
64+
}
65+
5666
// BlockByHash returns the given full block.
5767
//
5868
// Note that loading full blocks requires two requests. Use HeaderByHash

ethclient/gethclient/gethclient.go

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package gethclient
1919

2020
import (
2121
"context"
22+
"encoding/json"
2223
"fmt"
2324
"math/big"
2425
"runtime"
@@ -119,15 +120,6 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s
119120
return &result, err
120121
}
121122

122-
// OverrideAccount specifies the state of an account to be overridden.
123-
type OverrideAccount struct {
124-
Nonce uint64 `json:"nonce"`
125-
Code []byte `json:"code"`
126-
Balance *big.Int `json:"balance"`
127-
State map[common.Hash]common.Hash `json:"state"`
128-
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
129-
}
130-
131123
// CallContract executes a message call transaction, which is directly executed in the VM
132124
// of the node, but never mined into the blockchain.
133125
//
@@ -147,6 +139,28 @@ func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN
147139
return hex, err
148140
}
149141

142+
// CallContractWithBlockOverrides executes a message call transaction, which is directly executed
143+
// in the VM of the node, but never mined into the blockchain.
144+
//
145+
// blockNumber selects the block height at which the call runs. It can be nil, in which
146+
// case the code is taken from the latest known block. Note that state from very old
147+
// blocks might not be available.
148+
//
149+
// overrides specifies a map of contract states that should be overwritten before executing
150+
// the message call.
151+
//
152+
// blockOverrides specifies block fields exposed to the EVM that can be overridden for the call.
153+
//
154+
// Please use ethclient.CallContract instead if you don't need the override functionality.
155+
func (ec *Client) CallContractWithBlockOverrides(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount, blockOverrides BlockOverrides) ([]byte, error) {
156+
var hex hexutil.Bytes
157+
err := ec.c.CallContext(
158+
ctx, &hex, "eth_call", toCallArg(msg),
159+
toBlockNumArg(blockNumber), overrides, blockOverrides,
160+
)
161+
return hex, err
162+
}
163+
150164
// GCStats retrieves the current garbage collection stats from a geth node.
151165
func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
152166
var result debug.GCStats
@@ -175,6 +189,11 @@ func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) {
175189
return &result, err
176190
}
177191

192+
// SubscribeFullPendingTransactions subscribes to new pending transactions.
193+
func (ec *Client) SubscribeFullPendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (*rpc.ClientSubscription, error) {
194+
return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions", true)
195+
}
196+
178197
// SubscribePendingTransactions subscribes to new pending transactions.
179198
func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) {
180199
return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
@@ -224,6 +243,29 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
224243
return arg
225244
}
226245

246+
// OverrideAccount specifies the state of an account to be overridden.
247+
type OverrideAccount struct {
248+
// Nonce sets nonce of the account. Note: the nonce override will only
249+
// be applied when it is set to a non-zero value.
250+
Nonce uint64 `json:"nonce"`
251+
252+
// Code sets the contract code. The override will be applied
253+
// when the code is non-nil, i.e. setting empty code is possible
254+
// using an empty slice.
255+
Code []byte `json:"code"`
256+
257+
// Balance sets the account balance.
258+
Balance *big.Int `json:"balance"`
259+
260+
// State sets the complete storage. The override will be applied
261+
// when the given map is non-nil. Using an empty map wipes the
262+
// entire contract storage during the call.
263+
State map[common.Hash]common.Hash `json:"state"`
264+
265+
// StateDiff allows overriding individual storage slots.
266+
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
267+
}
268+
227269
func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
228270
if overrides == nil {
229271
return nil
@@ -247,3 +289,52 @@ func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
247289
}
248290
return &result
249291
}
292+
293+
// BlockOverrides specifies the set of header fields to override.
294+
type BlockOverrides struct {
295+
// Number overrides the block number.
296+
Number *big.Int
297+
// Difficulty overrides the block difficulty.
298+
Difficulty *big.Int
299+
// Time overrides the block timestamp. Time is applied only when
300+
// it is non-zero.
301+
Time uint64
302+
// GasLimit overrides the block gas limit. GasLimit is applied only when
303+
// it is non-zero.
304+
GasLimit uint64
305+
// Coinbase overrides the block coinbase. Coinbase is applied only when
306+
// it is different from the zero address.
307+
Coinbase common.Address
308+
// Random overrides the block extra data which feeds into the RANDOM opcode.
309+
// Random is applied only when it is a non-zero hash.
310+
Random common.Hash
311+
// BaseFee overrides the block base fee.
312+
BaseFee *big.Int
313+
}
314+
315+
func (o BlockOverrides) MarshalJSON() ([]byte, error) {
316+
type override struct {
317+
Number *hexutil.Big `json:"number,omitempty"`
318+
Difficulty *hexutil.Big `json:"difficulty,omitempty"`
319+
Time hexutil.Uint64 `json:"time,omitempty"`
320+
GasLimit hexutil.Uint64 `json:"gasLimit,omitempty"`
321+
Coinbase *common.Address `json:"coinbase,omitempty"`
322+
Random *common.Hash `json:"random,omitempty"`
323+
BaseFee *hexutil.Big `json:"baseFee,omitempty"`
324+
}
325+
326+
output := override{
327+
Number: (*hexutil.Big)(o.Number),
328+
Difficulty: (*hexutil.Big)(o.Difficulty),
329+
Time: hexutil.Uint64(o.Time),
330+
GasLimit: hexutil.Uint64(o.GasLimit),
331+
BaseFee: (*hexutil.Big)(o.BaseFee),
332+
}
333+
if o.Coinbase != (common.Address{}) {
334+
output.Coinbase = &o.Coinbase
335+
}
336+
if o.Random != (common.Hash{}) {
337+
output.Random = &o.Random
338+
}
339+
return json.Marshal(output)
340+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2021 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package gethclient
18+
19+
import (
20+
"encoding/json"
21+
"math/big"
22+
"testing"
23+
24+
"github.com/XinFinOrg/XDPoSChain/common"
25+
)
26+
27+
func TestOverrideAccountMarshal(t *testing.T) {
28+
om := map[common.Address]OverrideAccount{
29+
{0x11}: {
30+
// Zero-valued nonce is not overridden, but simply dropped by the encoder.
31+
Nonce: 0,
32+
},
33+
{0xaa}: {
34+
Nonce: 5,
35+
},
36+
{0xbb}: {
37+
Code: []byte{1},
38+
},
39+
{0xcc}: {
40+
// 'code', 'balance', 'state' should be set when input is
41+
// a non-nil but empty value.
42+
Code: []byte{},
43+
Balance: big.NewInt(0),
44+
State: map[common.Hash]common.Hash{},
45+
// For 'stateDiff' the behavior is different, empty map
46+
// is ignored because it makes no difference.
47+
StateDiff: map[common.Hash]common.Hash{},
48+
},
49+
}
50+
51+
marshalled, err := json.MarshalIndent(&om, "", " ")
52+
if err != nil {
53+
t.Fatalf("unexpected error: %v", err)
54+
}
55+
56+
expected := `{
57+
"0x1100000000000000000000000000000000000000": {
58+
"nonce": 0,
59+
"code": null,
60+
"balance": null,
61+
"state": null,
62+
"stateDiff": null
63+
},
64+
"0xaa00000000000000000000000000000000000000": {
65+
"nonce": 5,
66+
"code": null,
67+
"balance": null,
68+
"state": null,
69+
"stateDiff": null
70+
},
71+
"0xbb00000000000000000000000000000000000000": {
72+
"nonce": 0,
73+
"code": "AQ==",
74+
"balance": null,
75+
"state": null,
76+
"stateDiff": null
77+
},
78+
"0xcc00000000000000000000000000000000000000": {
79+
"nonce": 0,
80+
"code": "",
81+
"balance": 0,
82+
"state": {},
83+
"stateDiff": {}
84+
}
85+
}`
86+
87+
if string(marshalled) != expected {
88+
t.Error("wrong output:", string(marshalled))
89+
t.Error("want:", expected)
90+
}
91+
}
92+
93+
func TestBlockOverridesMarshal(t *testing.T) {
94+
for i, tt := range []struct {
95+
bo BlockOverrides
96+
want string
97+
}{
98+
{
99+
bo: BlockOverrides{},
100+
want: `{}`,
101+
},
102+
{
103+
bo: BlockOverrides{
104+
Coinbase: common.HexToAddress("0x1111111111111111111111111111111111111111"),
105+
},
106+
want: `{"coinbase":"0x1111111111111111111111111111111111111111"}`,
107+
},
108+
{
109+
bo: BlockOverrides{
110+
Number: big.NewInt(1),
111+
Difficulty: big.NewInt(2),
112+
Time: 3,
113+
GasLimit: 4,
114+
BaseFee: big.NewInt(5),
115+
},
116+
want: `{"number":"0x1","difficulty":"0x2","time":"0x3","gasLimit":"0x4","baseFee":"0x5"}`,
117+
},
118+
} {
119+
marshalled, err := json.Marshal(&tt.bo)
120+
if err != nil {
121+
t.Fatalf("unexpected error: %v", err)
122+
}
123+
if string(marshalled) != tt.want {
124+
t.Errorf("Testcase #%d failed. expected\n%s\ngot\n%s", i, tt.want, string(marshalled))
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)