mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 07:37:20 +00:00
ethclient: add support for eth_simulateV1 (#32856)
Adds ethclient support for the eth_simulateV1 RPC method, which allows simulating transactions on top of a base state without making changes to the blockchain. --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com>
This commit is contained in:
parent
5c535074ac
commit
c37bd67019
6 changed files with 575 additions and 94 deletions
|
|
@ -828,3 +828,89 @@ func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress {
|
|||
StateIndexRemaining: uint64(p.StateIndexRemaining),
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateOptions represents the options for eth_simulateV1.
|
||||
type SimulateOptions struct {
|
||||
BlockStateCalls []SimulateBlock `json:"blockStateCalls"`
|
||||
TraceTransfers bool `json:"traceTransfers"`
|
||||
Validation bool `json:"validation"`
|
||||
ReturnFullTransactions bool `json:"returnFullTransactions"`
|
||||
}
|
||||
|
||||
// SimulateBlock represents a batch of calls to be simulated.
|
||||
type SimulateBlock struct {
|
||||
BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"`
|
||||
StateOverrides map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"`
|
||||
Calls []ethereum.CallMsg `json:"calls"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler for SimulateBlock.
|
||||
func (s SimulateBlock) MarshalJSON() ([]byte, error) {
|
||||
type Alias struct {
|
||||
BlockOverrides *ethereum.BlockOverrides `json:"blockOverrides,omitempty"`
|
||||
StateOverrides map[common.Address]ethereum.OverrideAccount `json:"stateOverrides,omitempty"`
|
||||
Calls []interface{} `json:"calls"`
|
||||
}
|
||||
calls := make([]interface{}, len(s.Calls))
|
||||
for i, call := range s.Calls {
|
||||
calls[i] = toCallArg(call)
|
||||
}
|
||||
return json.Marshal(Alias{
|
||||
BlockOverrides: s.BlockOverrides,
|
||||
StateOverrides: s.StateOverrides,
|
||||
Calls: calls,
|
||||
})
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type SimulateCallResult -field-override simulateCallResultMarshaling -out gen_simulate_call_result.go
|
||||
|
||||
// SimulateCallResult is the result of a simulated call.
|
||||
type SimulateCallResult struct {
|
||||
ReturnValue []byte `json:"returnData"`
|
||||
Logs []*types.Log `json:"logs"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
Status uint64 `json:"status"`
|
||||
Error *CallError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type simulateCallResultMarshaling struct {
|
||||
ReturnValue hexutil.Bytes
|
||||
GasUsed hexutil.Uint64
|
||||
Status hexutil.Uint64
|
||||
}
|
||||
|
||||
// CallError represents an error from a simulated call.
|
||||
type CallError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type SimulateBlockResult -field-override simulateBlockResultMarshaling -out gen_simulate_block_result.go
|
||||
|
||||
// SimulateBlockResult represents the result of a simulated block.
|
||||
type SimulateBlockResult struct {
|
||||
Number *big.Int `json:"number"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Timestamp uint64 `json:"timestamp"`
|
||||
GasLimit uint64 `json:"gasLimit"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
FeeRecipient common.Address `json:"miner"`
|
||||
BaseFeePerGas *big.Int `json:"baseFeePerGas,omitempty"`
|
||||
Calls []SimulateCallResult `json:"calls"`
|
||||
}
|
||||
|
||||
type simulateBlockResultMarshaling struct {
|
||||
Number *hexutil.Big
|
||||
Timestamp hexutil.Uint64
|
||||
GasLimit hexutil.Uint64
|
||||
GasUsed hexutil.Uint64
|
||||
BaseFeePerGas *hexutil.Big
|
||||
}
|
||||
|
||||
// SimulateV1 executes transactions on top of a base state.
|
||||
func (ec *Client) SimulateV1(ctx context.Context, opts SimulateOptions, blockNrOrHash *rpc.BlockNumberOrHash) ([]SimulateBlockResult, error) {
|
||||
var result []SimulateBlockResult
|
||||
err := ec.c.CallContext(ctx, &result, "eth_simulateV1", opts, blockNrOrHash)
|
||||
return result, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -754,3 +754,250 @@ func ExampleRevertErrorData() {
|
|||
// revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72
|
||||
// message: user error
|
||||
}
|
||||
|
||||
func TestSimulateV1(t *testing.T) {
|
||||
backend, _, err := newTestBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test backend: %v", err)
|
||||
}
|
||||
defer backend.Close()
|
||||
|
||||
client := ethclient.NewClient(backend.Attach())
|
||||
defer client.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Get current base fee
|
||||
header, err := client.HeaderByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get header: %v", err)
|
||||
}
|
||||
|
||||
// Simple test: transfer ETH from one account to another
|
||||
from := testAddr
|
||||
to := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
value := big.NewInt(100)
|
||||
gas := uint64(100000)
|
||||
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||
|
||||
opts := ethclient.SimulateOptions{
|
||||
BlockStateCalls: []ethclient.SimulateBlock{
|
||||
{
|
||||
Calls: []ethereum.CallMsg{
|
||||
{
|
||||
From: from,
|
||||
To: &to,
|
||||
Value: value,
|
||||
Gas: gas,
|
||||
GasFeeCap: maxFeePerGas,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Validation: true,
|
||||
}
|
||||
|
||||
results, err := client.SimulateV1(ctx, opts, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("SimulateV1 failed: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected 1 block result, got %d", len(results))
|
||||
}
|
||||
|
||||
if len(results[0].Calls) != 1 {
|
||||
t.Fatalf("expected 1 call result, got %d", len(results[0].Calls))
|
||||
}
|
||||
|
||||
// Check that the transaction succeeded
|
||||
if results[0].Calls[0].Status != 1 {
|
||||
t.Errorf("expected status 1 (success), got %d", results[0].Calls[0].Status)
|
||||
}
|
||||
|
||||
if results[0].Calls[0].Error != nil {
|
||||
t.Errorf("expected no error, got %v", results[0].Calls[0].Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimulateV1WithBlockOverrides(t *testing.T) {
|
||||
backend, _, err := newTestBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test backend: %v", err)
|
||||
}
|
||||
defer backend.Close()
|
||||
|
||||
client := ethclient.NewClient(backend.Attach())
|
||||
defer client.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Get current base fee
|
||||
header, err := client.HeaderByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get header: %v", err)
|
||||
}
|
||||
|
||||
from := testAddr
|
||||
to := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
value := big.NewInt(100)
|
||||
gas := uint64(100000)
|
||||
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||
|
||||
// Override timestamp only
|
||||
timestamp := uint64(1234567890)
|
||||
|
||||
opts := ethclient.SimulateOptions{
|
||||
BlockStateCalls: []ethclient.SimulateBlock{
|
||||
{
|
||||
BlockOverrides: ðereum.BlockOverrides{
|
||||
Time: timestamp,
|
||||
},
|
||||
Calls: []ethereum.CallMsg{
|
||||
{
|
||||
From: from,
|
||||
To: &to,
|
||||
Value: value,
|
||||
Gas: gas,
|
||||
GasFeeCap: maxFeePerGas,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Validation: true,
|
||||
}
|
||||
|
||||
results, err := client.SimulateV1(ctx, opts, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("SimulateV1 with block overrides failed: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected 1 block result, got %d", len(results))
|
||||
}
|
||||
|
||||
// Verify the timestamp was overridden
|
||||
if results[0].Timestamp != timestamp {
|
||||
t.Errorf("expected timestamp %d, got %d", timestamp, results[0].Timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimulateV1WithStateOverrides(t *testing.T) {
|
||||
backend, _, err := newTestBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test backend: %v", err)
|
||||
}
|
||||
defer backend.Close()
|
||||
|
||||
client := ethclient.NewClient(backend.Attach())
|
||||
defer client.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Get current base fee
|
||||
header, err := client.HeaderByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get header: %v", err)
|
||||
}
|
||||
|
||||
from := testAddr
|
||||
to := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
value := big.NewInt(1000000000000000000) // 1 ETH
|
||||
gas := uint64(100000)
|
||||
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||
|
||||
// Override the balance of the 'from' address
|
||||
balanceStr := "1000000000000000000000"
|
||||
balance := new(big.Int)
|
||||
balance.SetString(balanceStr, 10)
|
||||
|
||||
stateOverrides := map[common.Address]ethereum.OverrideAccount{
|
||||
from: {
|
||||
Balance: balance,
|
||||
},
|
||||
}
|
||||
|
||||
opts := ethclient.SimulateOptions{
|
||||
BlockStateCalls: []ethclient.SimulateBlock{
|
||||
{
|
||||
StateOverrides: stateOverrides,
|
||||
Calls: []ethereum.CallMsg{
|
||||
{
|
||||
From: from,
|
||||
To: &to,
|
||||
Value: value,
|
||||
Gas: gas,
|
||||
GasFeeCap: maxFeePerGas,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Validation: true,
|
||||
}
|
||||
|
||||
results, err := client.SimulateV1(ctx, opts, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("SimulateV1 with state overrides failed: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected 1 block result, got %d", len(results))
|
||||
}
|
||||
|
||||
if results[0].Calls[0].Status != 1 {
|
||||
t.Errorf("expected status 1 (success), got %d", results[0].Calls[0].Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimulateV1WithBlockNumberOrHash(t *testing.T) {
|
||||
backend, _, err := newTestBackend(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test backend: %v", err)
|
||||
}
|
||||
defer backend.Close()
|
||||
|
||||
client := ethclient.NewClient(backend.Attach())
|
||||
defer client.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Get current base fee
|
||||
header, err := client.HeaderByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get header: %v", err)
|
||||
}
|
||||
|
||||
from := testAddr
|
||||
to := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||
value := big.NewInt(100)
|
||||
gas := uint64(100000)
|
||||
maxFeePerGas := new(big.Int).Mul(header.BaseFee, big.NewInt(2))
|
||||
|
||||
opts := ethclient.SimulateOptions{
|
||||
BlockStateCalls: []ethclient.SimulateBlock{
|
||||
{
|
||||
Calls: []ethereum.CallMsg{
|
||||
{
|
||||
From: from,
|
||||
To: &to,
|
||||
Value: value,
|
||||
Gas: gas,
|
||||
GasFeeCap: maxFeePerGas,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Validation: true,
|
||||
}
|
||||
|
||||
// Simulate on the latest block
|
||||
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||
results, err := client.SimulateV1(ctx, opts, &latest)
|
||||
if err != nil {
|
||||
t.Fatalf("SimulateV1 with latest block failed: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected 1 block result, got %d", len(results))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
80
ethclient/gen_simulate_block_result.go
Normal file
80
ethclient/gen_simulate_block_result.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package ethclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*simulateBlockResultMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (s SimulateBlockResult) MarshalJSON() ([]byte, error) {
|
||||
type SimulateBlockResult struct {
|
||||
Number *hexutil.Big `json:"number"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Timestamp hexutil.Uint64 `json:"timestamp"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
||||
FeeRecipient common.Address `json:"miner"`
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||
Calls []SimulateCallResult `json:"calls"`
|
||||
}
|
||||
var enc SimulateBlockResult
|
||||
enc.Number = (*hexutil.Big)(s.Number)
|
||||
enc.Hash = s.Hash
|
||||
enc.Timestamp = hexutil.Uint64(s.Timestamp)
|
||||
enc.GasLimit = hexutil.Uint64(s.GasLimit)
|
||||
enc.GasUsed = hexutil.Uint64(s.GasUsed)
|
||||
enc.FeeRecipient = s.FeeRecipient
|
||||
enc.BaseFeePerGas = (*hexutil.Big)(s.BaseFeePerGas)
|
||||
enc.Calls = s.Calls
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (s *SimulateBlockResult) UnmarshalJSON(input []byte) error {
|
||||
type SimulateBlockResult struct {
|
||||
Number *hexutil.Big `json:"number"`
|
||||
Hash *common.Hash `json:"hash"`
|
||||
Timestamp *hexutil.Uint64 `json:"timestamp"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed"`
|
||||
FeeRecipient *common.Address `json:"miner"`
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||
Calls []SimulateCallResult `json:"calls"`
|
||||
}
|
||||
var dec SimulateBlockResult
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Number != nil {
|
||||
s.Number = (*big.Int)(dec.Number)
|
||||
}
|
||||
if dec.Hash != nil {
|
||||
s.Hash = *dec.Hash
|
||||
}
|
||||
if dec.Timestamp != nil {
|
||||
s.Timestamp = uint64(*dec.Timestamp)
|
||||
}
|
||||
if dec.GasLimit != nil {
|
||||
s.GasLimit = uint64(*dec.GasLimit)
|
||||
}
|
||||
if dec.GasUsed != nil {
|
||||
s.GasUsed = uint64(*dec.GasUsed)
|
||||
}
|
||||
if dec.FeeRecipient != nil {
|
||||
s.FeeRecipient = *dec.FeeRecipient
|
||||
}
|
||||
if dec.BaseFeePerGas != nil {
|
||||
s.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
|
||||
}
|
||||
if dec.Calls != nil {
|
||||
s.Calls = dec.Calls
|
||||
}
|
||||
return nil
|
||||
}
|
||||
61
ethclient/gen_simulate_call_result.go
Normal file
61
ethclient/gen_simulate_call_result.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package ethclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var _ = (*simulateCallResultMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (s SimulateCallResult) MarshalJSON() ([]byte, error) {
|
||||
type SimulateCallResult struct {
|
||||
ReturnValue hexutil.Bytes `json:"returnData"`
|
||||
Logs []*types.Log `json:"logs"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
||||
Status hexutil.Uint64 `json:"status"`
|
||||
Error *CallError `json:"error,omitempty"`
|
||||
}
|
||||
var enc SimulateCallResult
|
||||
enc.ReturnValue = s.ReturnValue
|
||||
enc.Logs = s.Logs
|
||||
enc.GasUsed = hexutil.Uint64(s.GasUsed)
|
||||
enc.Status = hexutil.Uint64(s.Status)
|
||||
enc.Error = s.Error
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (s *SimulateCallResult) UnmarshalJSON(input []byte) error {
|
||||
type SimulateCallResult struct {
|
||||
ReturnValue *hexutil.Bytes `json:"returnData"`
|
||||
Logs []*types.Log `json:"logs"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed"`
|
||||
Status *hexutil.Uint64 `json:"status"`
|
||||
Error *CallError `json:"error,omitempty"`
|
||||
}
|
||||
var dec SimulateCallResult
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.ReturnValue != nil {
|
||||
s.ReturnValue = *dec.ReturnValue
|
||||
}
|
||||
if dec.Logs != nil {
|
||||
s.Logs = dec.Logs
|
||||
}
|
||||
if dec.GasUsed != nil {
|
||||
s.GasUsed = uint64(*dec.GasUsed)
|
||||
}
|
||||
if dec.Status != nil {
|
||||
s.Status = uint64(*dec.Status)
|
||||
}
|
||||
if dec.Error != nil {
|
||||
s.Error = dec.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ package gethclient
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"runtime"
|
||||
|
|
@ -280,97 +279,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
|||
return arg
|
||||
}
|
||||
|
||||
// OverrideAccount specifies the state of an account to be overridden.
|
||||
type OverrideAccount struct {
|
||||
// Nonce sets nonce of the account. Note: the nonce override will only
|
||||
// be applied when it is set to a non-zero value.
|
||||
Nonce uint64
|
||||
// OverrideAccount is an alias for ethereum.OverrideAccount.
|
||||
type OverrideAccount = ethereum.OverrideAccount
|
||||
|
||||
// Code sets the contract code. The override will be applied
|
||||
// when the code is non-nil, i.e. setting empty code is possible
|
||||
// using an empty slice.
|
||||
Code []byte
|
||||
|
||||
// Balance sets the account balance.
|
||||
Balance *big.Int
|
||||
|
||||
// State sets the complete storage. The override will be applied
|
||||
// when the given map is non-nil. Using an empty map wipes the
|
||||
// entire contract storage during the call.
|
||||
State map[common.Hash]common.Hash
|
||||
|
||||
// StateDiff allows overriding individual storage slots.
|
||||
StateDiff 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"`
|
||||
}
|
||||
|
||||
output := acc{
|
||||
Nonce: hexutil.Uint64(a.Nonce),
|
||||
Balance: (*hexutil.Big)(a.Balance),
|
||||
StateDiff: a.StateDiff,
|
||||
}
|
||||
if a.Code != nil {
|
||||
output.Code = hexutil.Encode(a.Code)
|
||||
}
|
||||
if a.State != nil {
|
||||
output.State = a.State
|
||||
}
|
||||
return json.Marshal(output)
|
||||
}
|
||||
|
||||
// BlockOverrides specifies the set of header fields to override.
|
||||
type BlockOverrides struct {
|
||||
// Number overrides the block number.
|
||||
Number *big.Int
|
||||
// Difficulty overrides the block difficulty.
|
||||
Difficulty *big.Int
|
||||
// Time overrides the block timestamp. Time is applied only when
|
||||
// it is non-zero.
|
||||
Time uint64
|
||||
// GasLimit overrides the block gas limit. GasLimit is applied only when
|
||||
// it is non-zero.
|
||||
GasLimit uint64
|
||||
// Coinbase overrides the block coinbase. Coinbase is applied only when
|
||||
// it is different from the zero address.
|
||||
Coinbase common.Address
|
||||
// Random overrides the block extra data which feeds into the RANDOM opcode.
|
||||
// Random is applied only when it is a non-zero hash.
|
||||
Random common.Hash
|
||||
// BaseFee overrides the block base fee.
|
||||
BaseFee *big.Int
|
||||
}
|
||||
|
||||
func (o BlockOverrides) MarshalJSON() ([]byte, error) {
|
||||
type override struct {
|
||||
Number *hexutil.Big `json:"number,omitempty"`
|
||||
Difficulty *hexutil.Big `json:"difficulty,omitempty"`
|
||||
Time hexutil.Uint64 `json:"time,omitempty"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit,omitempty"`
|
||||
Coinbase *common.Address `json:"feeRecipient,omitempty"`
|
||||
Random *common.Hash `json:"prevRandao,omitempty"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||
}
|
||||
|
||||
output := override{
|
||||
Number: (*hexutil.Big)(o.Number),
|
||||
Difficulty: (*hexutil.Big)(o.Difficulty),
|
||||
Time: hexutil.Uint64(o.Time),
|
||||
GasLimit: hexutil.Uint64(o.GasLimit),
|
||||
BaseFee: (*hexutil.Big)(o.BaseFee),
|
||||
}
|
||||
if o.Coinbase != (common.Address{}) {
|
||||
output.Coinbase = &o.Coinbase
|
||||
}
|
||||
if o.Random != (common.Hash{}) {
|
||||
output.Random = &o.Random
|
||||
}
|
||||
return json.Marshal(output)
|
||||
}
|
||||
// BlockOverrides is an alias for ethereum.BlockOverrides.
|
||||
type BlockOverrides = ethereum.BlockOverrides
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ package ethereum
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
|
|
@ -293,3 +295,98 @@ type BlockNumberReader interface {
|
|||
type ChainIDReader interface {
|
||||
ChainID(ctx context.Context) (*big.Int, error)
|
||||
}
|
||||
|
||||
// OverrideAccount specifies the state of an account to be overridden.
|
||||
type OverrideAccount struct {
|
||||
// Nonce sets nonce of the account. Note: the nonce override will only
|
||||
// be applied when it is set to a non-zero value.
|
||||
Nonce uint64
|
||||
|
||||
// Code sets the contract code. The override will be applied
|
||||
// when the code is non-nil, i.e. setting empty code is possible
|
||||
// using an empty slice.
|
||||
Code []byte
|
||||
|
||||
// Balance sets the account balance.
|
||||
Balance *big.Int
|
||||
|
||||
// State sets the complete storage. The override will be applied
|
||||
// when the given map is non-nil. Using an empty map wipes the
|
||||
// entire contract storage during the call.
|
||||
State map[common.Hash]common.Hash
|
||||
|
||||
// StateDiff allows overriding individual storage slots.
|
||||
StateDiff 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"`
|
||||
}
|
||||
|
||||
output := acc{
|
||||
Nonce: hexutil.Uint64(a.Nonce),
|
||||
Balance: (*hexutil.Big)(a.Balance),
|
||||
StateDiff: a.StateDiff,
|
||||
}
|
||||
if a.Code != nil {
|
||||
output.Code = hexutil.Encode(a.Code)
|
||||
}
|
||||
if a.State != nil {
|
||||
output.State = a.State
|
||||
}
|
||||
return json.Marshal(output)
|
||||
}
|
||||
|
||||
// BlockOverrides specifies the set of header fields to override.
|
||||
type BlockOverrides struct {
|
||||
// Number overrides the block number.
|
||||
Number *big.Int
|
||||
// Difficulty overrides the block difficulty.
|
||||
Difficulty *big.Int
|
||||
// Time overrides the block timestamp. Time is applied only when
|
||||
// it is non-zero.
|
||||
Time uint64
|
||||
// GasLimit overrides the block gas limit. GasLimit is applied only when
|
||||
// it is non-zero.
|
||||
GasLimit uint64
|
||||
// Coinbase overrides the block coinbase. Coinbase is applied only when
|
||||
// it is different from the zero address.
|
||||
Coinbase common.Address
|
||||
// Random overrides the block extra data which feeds into the RANDOM opcode.
|
||||
// Random is applied only when it is a non-zero hash.
|
||||
Random common.Hash
|
||||
// BaseFee overrides the block base fee.
|
||||
BaseFee *big.Int
|
||||
}
|
||||
|
||||
func (o BlockOverrides) MarshalJSON() ([]byte, error) {
|
||||
type override struct {
|
||||
Number *hexutil.Big `json:"number,omitempty"`
|
||||
Difficulty *hexutil.Big `json:"difficulty,omitempty"`
|
||||
Time hexutil.Uint64 `json:"time,omitempty"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit,omitempty"`
|
||||
Coinbase *common.Address `json:"feeRecipient,omitempty"`
|
||||
Random *common.Hash `json:"prevRandao,omitempty"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||
}
|
||||
|
||||
output := override{
|
||||
Number: (*hexutil.Big)(o.Number),
|
||||
Difficulty: (*hexutil.Big)(o.Difficulty),
|
||||
Time: hexutil.Uint64(o.Time),
|
||||
GasLimit: hexutil.Uint64(o.GasLimit),
|
||||
BaseFee: (*hexutil.Big)(o.BaseFee),
|
||||
}
|
||||
if o.Coinbase != (common.Address{}) {
|
||||
output.Coinbase = &o.Coinbase
|
||||
}
|
||||
if o.Random != (common.Hash{}) {
|
||||
output.Random = &o.Random
|
||||
}
|
||||
return json.Marshal(output)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue