Skip to content

Commit f9d0f1f

Browse files
simonovic86fjlholiman
authored andcommitted
ethclient/gethclient: fix bugs in override object encoding (ethereum#25616)
This fixes a bug where contract code would be overridden to empty code ("0x") when the Code field of OverrideAccount was left nil. The change also cleans up the encoding of overrides to only send necessary fields, and improves documentation. Fixes ethereum#25615 Co-authored-by: Felix Lange <[email protected]> Co-authored-by: Martin Holst Swende <[email protected]>
1 parent 85b8130 commit f9d0f1f

File tree

2 files changed

+97
-32
lines changed

2 files changed

+97
-32
lines changed

ethclient/gethclient/gethclient.go

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

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

121-
// OverrideAccount specifies the state of an account to be overridden.
122-
type OverrideAccount struct {
123-
Nonce uint64 `json:"nonce"`
124-
Code []byte `json:"code"`
125-
Balance *big.Int `json:"balance"`
126-
State map[common.Hash]common.Hash `json:"state"`
127-
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
128-
}
129-
130122
// CallContract executes a message call transaction, which is directly executed in the VM
131123
// of the node, but never mined into the blockchain.
132124
//
@@ -141,7 +133,7 @@ func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN
141133
var hex hexutil.Bytes
142134
err := ec.c.CallContext(
143135
ctx, &hex, "eth_call", toCallArg(msg),
144-
toBlockNumArg(blockNumber), toOverrideMap(overrides),
136+
toBlockNumArg(blockNumber), overrides,
145137
)
146138
return hex, err
147139
}
@@ -218,26 +210,48 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
218210
return arg
219211
}
220212

221-
func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
222-
if overrides == nil {
223-
return nil
224-
}
225-
type overrideAccount struct {
226-
Nonce hexutil.Uint64 `json:"nonce"`
227-
Code hexutil.Bytes `json:"code"`
228-
Balance *hexutil.Big `json:"balance"`
229-
State map[common.Hash]common.Hash `json:"state"`
230-
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
231-
}
232-
result := make(map[common.Address]overrideAccount)
233-
for addr, override := range *overrides {
234-
result[addr] = overrideAccount{
235-
Nonce: hexutil.Uint64(override.Nonce),
236-
Code: override.Code,
237-
Balance: (*hexutil.Big)(override.Balance),
238-
State: override.State,
239-
StateDiff: override.StateDiff,
240-
}
241-
}
242-
return &result
213+
// OverrideAccount specifies the state of an account to be overridden.
214+
type OverrideAccount struct {
215+
// Nonce sets nonce of the account. Note: the nonce override will only
216+
// be applied when it is set to a non-zero value.
217+
Nonce uint64
218+
219+
// Code sets the contract code. The override will be applied
220+
// when the code is non-nil, i.e. setting empty code is possible
221+
// using an empty slice.
222+
Code []byte
223+
224+
// Balance sets the account balance.
225+
Balance *big.Int
226+
227+
// State sets the complete storage. The override will be applied
228+
// when the given map is non-nil. Using an empty map wipes the
229+
// entire contract storage during the call.
230+
State map[common.Hash]common.Hash
231+
232+
// StateDiff allows overriding individual storage slots.
233+
StateDiff map[common.Hash]common.Hash
234+
}
235+
236+
func (a OverrideAccount) MarshalJSON() ([]byte, error) {
237+
type acc struct {
238+
Nonce hexutil.Uint64 `json:"nonce,omitempty"`
239+
Code string `json:"code,omitempty"`
240+
Balance *hexutil.Big `json:"balance,omitempty"`
241+
State interface{} `json:"state,omitempty"`
242+
StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"`
243+
}
244+
245+
output := acc{
246+
Nonce: hexutil.Uint64(a.Nonce),
247+
Balance: (*hexutil.Big)(a.Balance),
248+
StateDiff: a.StateDiff,
249+
}
250+
if a.Code != nil {
251+
output.Code = hexutil.Encode(a.Code)
252+
}
253+
if a.State != nil {
254+
output.State = a.State
255+
}
256+
return json.Marshal(output)
243257
}

ethclient/gethclient/gethclient_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package gethclient
1919
import (
2020
"bytes"
2121
"context"
22+
"encoding/json"
2223
"math/big"
2324
"testing"
2425

@@ -322,3 +323,53 @@ func testCallContract(t *testing.T, client *rpc.Client) {
322323
t.Fatalf("unexpected error: %v", err)
323324
}
324325
}
326+
327+
func TestOverrideAccountMarshal(t *testing.T) {
328+
om := map[common.Address]OverrideAccount{
329+
common.Address{0x11}: OverrideAccount{
330+
// Zero-valued nonce is not overriddden, but simply dropped by the encoder.
331+
Nonce: 0,
332+
},
333+
common.Address{0xaa}: OverrideAccount{
334+
Nonce: 5,
335+
},
336+
common.Address{0xbb}: OverrideAccount{
337+
Code: []byte{1},
338+
},
339+
common.Address{0xcc}: OverrideAccount{
340+
// 'code', 'balance', 'state' should be set when input is
341+
// a non-nil but empty value.
342+
Code: []byte{},
343+
Balance: big.NewInt(0),
344+
State: map[common.Hash]common.Hash{},
345+
// For 'stateDiff' the behavior is different, empty map
346+
// is ignored because it makes no difference.
347+
StateDiff: map[common.Hash]common.Hash{},
348+
},
349+
}
350+
351+
marshalled, err := json.MarshalIndent(&om, "", " ")
352+
if err != nil {
353+
t.Fatalf("unexpected error: %v", err)
354+
}
355+
356+
expected := `{
357+
"0x1100000000000000000000000000000000000000": {},
358+
"0xaa00000000000000000000000000000000000000": {
359+
"nonce": "0x5"
360+
},
361+
"0xbb00000000000000000000000000000000000000": {
362+
"code": "0x01"
363+
},
364+
"0xcc00000000000000000000000000000000000000": {
365+
"code": "0x",
366+
"balance": "0x0",
367+
"state": {}
368+
}
369+
}`
370+
371+
if string(marshalled) != expected {
372+
t.Error("wrong output:", string(marshalled))
373+
t.Error("want:", expected)
374+
}
375+
}

0 commit comments

Comments
 (0)