Skip to content

Commit 065f82a

Browse files
authored
accounts, ethclient: minor tweaks on the new simulated backend (#28799)
* accounts, ethclient: minor tweaks on the new simulated backend * ethclient/simulated: add an initial batch of gas options * accounts, ethclient: remove mandatory gasLimit constructor param * accounts, ethclient: minor option naming tweaks
1 parent 7280a5b commit 065f82a

File tree

6 files changed

+158
-51
lines changed

6 files changed

+158
-51
lines changed

accounts/abi/bind/backends/simulated.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parentHash common.Hash) err
4444
// Deprecated: please use simulated.Backend from package
4545
// github.com/ethereum/go-ethereum/ethclient/simulated instead.
4646
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
47-
b := simulated.New(alloc, gasLimit)
47+
b := simulated.NewBackend(alloc, simulated.WithBlockGasLimit(gasLimit))
4848
return &SimulatedBackend{
4949
Backend: b,
5050
Client: b.Client(),

accounts/abi/bind/util_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,10 @@ var waitDeployedTests = map[string]struct {
5656
func TestWaitDeployed(t *testing.T) {
5757
t.Parallel()
5858
for name, test := range waitDeployedTests {
59-
backend := simulated.New(
59+
backend := simulated.NewBackend(
6060
core.GenesisAlloc{
6161
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
6262
},
63-
10000000,
6463
)
6564
defer backend.Close()
6665

@@ -102,11 +101,10 @@ func TestWaitDeployed(t *testing.T) {
102101
}
103102

104103
func TestWaitDeployedCornerCases(t *testing.T) {
105-
backend := simulated.New(
104+
backend := simulated.NewBackend(
106105
core.GenesisAlloc{
107106
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
108107
},
109-
10000000,
110108
)
111109
defer backend.Close()
112110

ethclient/simulated/backend.go

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,6 @@ import (
3434
"github.com/ethereum/go-ethereum/rpc"
3535
)
3636

37-
// Backend is a simulated blockchain. You can use it to test your contracts or
38-
// other code that interacts with the Ethereum chain.
39-
type Backend struct {
40-
eth *eth.Ethereum
41-
beacon *catalyst.SimulatedBeacon
42-
client simClient
43-
}
44-
45-
// simClient wraps ethclient. This exists to prevent extracting ethclient.Client
46-
// from the Client interface returned by Backend.
47-
type simClient struct {
48-
*ethclient.Client
49-
}
50-
5137
// Client exposes the methods provided by the Ethereum RPC client.
5238
type Client interface {
5339
ethereum.BlockNumberReader
@@ -66,70 +52,81 @@ type Client interface {
6652
ethereum.ChainIDReader
6753
}
6854

69-
// New creates a new binding backend using a simulated blockchain
70-
// for testing purposes.
55+
// simClient wraps ethclient. This exists to prevent extracting ethclient.Client
56+
// from the Client interface returned by Backend.
57+
type simClient struct {
58+
*ethclient.Client
59+
}
60+
61+
// Backend is a simulated blockchain. You can use it to test your contracts or
62+
// other code that interacts with the Ethereum chain.
63+
type Backend struct {
64+
eth *eth.Ethereum
65+
beacon *catalyst.SimulatedBeacon
66+
client simClient
67+
}
68+
69+
// NewBackend creates a new simulated blockchain that can be used as a backend for
70+
// contract bindings in unit tests.
71+
//
7172
// A simulated backend always uses chainID 1337.
72-
func New(alloc core.GenesisAlloc, gasLimit uint64) *Backend {
73-
// Setup the node object
73+
func NewBackend(alloc core.GenesisAlloc, options ...func(nodeConf *node.Config, ethConf *ethconfig.Config)) *Backend {
74+
// Create the default configurations for the outer node shell and the Ethereum
75+
// service to mutate with the options afterwards
7476
nodeConf := node.DefaultConfig
7577
nodeConf.DataDir = ""
7678
nodeConf.P2P = p2p.Config{NoDiscovery: true}
77-
stack, err := node.New(&nodeConf)
78-
if err != nil {
79-
// This should never happen, if it does, please open an issue
80-
panic(err)
81-
}
8279

83-
// Setup ethereum
84-
genesis := core.Genesis{
80+
ethConf := ethconfig.Defaults
81+
ethConf.Genesis = &core.Genesis{
8582
Config: params.AllDevChainProtocolChanges,
86-
GasLimit: gasLimit,
83+
GasLimit: ethconfig.Defaults.Miner.GasCeil,
8784
Alloc: alloc,
8885
}
89-
conf := ethconfig.Defaults
90-
conf.Genesis = &genesis
91-
conf.SyncMode = downloader.FullSync
92-
conf.TxPool.NoLocals = true
93-
sim, err := newWithNode(stack, &conf, 0)
86+
ethConf.SyncMode = downloader.FullSync
87+
ethConf.TxPool.NoLocals = true
88+
89+
for _, option := range options {
90+
option(&nodeConf, &ethConf)
91+
}
92+
// Assemble the Ethereum stack to run the chain with
93+
stack, err := node.New(&nodeConf)
94+
if err != nil {
95+
panic(err) // this should never happen
96+
}
97+
sim, err := newWithNode(stack, &ethConf, 0)
9498
if err != nil {
95-
// This should never happen, if it does, please open an issue
96-
panic(err)
99+
panic(err) // this should never happen
97100
}
98101
return sim
99102
}
100103

101-
// newWithNode sets up a simulated backend on an existing node
102-
// this allows users to do persistent simulations.
103-
// The provided node must not be started and will be started by newWithNode
104+
// newWithNode sets up a simulated backend on an existing node. The provided node
105+
// must not be started and will be started by this method.
104106
func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) {
105107
backend, err := eth.New(stack, conf)
106108
if err != nil {
107109
return nil, err
108110
}
109-
110111
// Register the filter system
111112
filterSystem := filters.NewFilterSystem(backend.APIBackend, filters.Config{})
112113
stack.RegisterAPIs([]rpc.API{{
113114
Namespace: "eth",
114115
Service: filters.NewFilterAPI(filterSystem, false),
115116
}})
116-
117117
// Start the node
118118
if err := stack.Start(); err != nil {
119119
return nil, err
120120
}
121-
122121
// Set up the simulated beacon
123122
beacon, err := catalyst.NewSimulatedBeacon(blockPeriod, backend)
124123
if err != nil {
125124
return nil, err
126125
}
127-
128126
// Reorg our chain back to genesis
129127
if err := beacon.Fork(backend.BlockChain().GetCanonicalHash(0)); err != nil {
130128
return nil, err
131129
}
132-
133130
return &Backend{
134131
eth: backend,
135132
beacon: beacon,

ethclient/simulated/backend_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ var (
4040
)
4141

4242
func simTestBackend(testAddr common.Address) *Backend {
43-
return New(
43+
return NewBackend(
4444
core.GenesisAlloc{
4545
testAddr: {Balance: big.NewInt(10000000000000000)},
46-
}, 10000000,
46+
},
4747
)
4848
}
4949

@@ -70,8 +70,8 @@ func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) {
7070
return types.SignTx(tx, types.LatestSignerForChainID(chainid), key)
7171
}
7272

73-
func TestNewSim(t *testing.T) {
74-
sim := New(core.GenesisAlloc{}, 30_000_000)
73+
func TestNewBackend(t *testing.T) {
74+
sim := NewBackend(core.GenesisAlloc{})
7575
defer sim.Close()
7676

7777
client := sim.Client()
@@ -94,7 +94,7 @@ func TestNewSim(t *testing.T) {
9494
}
9595

9696
func TestAdjustTime(t *testing.T) {
97-
sim := New(core.GenesisAlloc{}, 10_000_000)
97+
sim := NewBackend(core.GenesisAlloc{})
9898
defer sim.Close()
9999

100100
client := sim.Client()

ethclient/simulated/options.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2024 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 simulated
18+
19+
import (
20+
"github.com/ethereum/go-ethereum/eth/ethconfig"
21+
"github.com/ethereum/go-ethereum/node"
22+
)
23+
24+
// WithBlockGasLimit configures the simulated backend to target a specific gas limit
25+
// when producing blocks.
26+
func WithBlockGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
27+
return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
28+
ethConf.Genesis.GasLimit = gaslimit
29+
ethConf.Miner.GasCeil = gaslimit
30+
}
31+
}
32+
33+
// WithCallGasLimit configures the simulated backend to cap eth_calls to a specific
34+
// gas limit when running client operations.
35+
func WithCallGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
36+
return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
37+
ethConf.RPCGasCap = gaslimit
38+
}
39+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2024 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 simulated
18+
19+
import (
20+
"context"
21+
"math/big"
22+
"strings"
23+
"testing"
24+
25+
"github.com/ethereum/go-ethereum"
26+
"github.com/ethereum/go-ethereum/core"
27+
"github.com/ethereum/go-ethereum/params"
28+
)
29+
30+
// Tests that the simulator starts with the initial gas limit in the genesis block,
31+
// and that it keeps the same target value.
32+
func TestWithBlockGasLimitOption(t *testing.T) {
33+
// Construct a simulator, targeting a different gas limit
34+
sim := NewBackend(core.GenesisAlloc{}, WithBlockGasLimit(12_345_678))
35+
defer sim.Close()
36+
37+
client := sim.Client()
38+
genesis, err := client.BlockByNumber(context.Background(), big.NewInt(0))
39+
if err != nil {
40+
t.Fatalf("failed to retrieve genesis block: %v", err)
41+
}
42+
if genesis.GasLimit() != 12_345_678 {
43+
t.Errorf("genesis gas limit mismatch: have %v, want %v", genesis.GasLimit(), 12_345_678)
44+
}
45+
// Produce a number of blocks and verify the locked in gas target
46+
sim.Commit()
47+
head, err := client.BlockByNumber(context.Background(), big.NewInt(1))
48+
if err != nil {
49+
t.Fatalf("failed to retrieve head block: %v", err)
50+
}
51+
if head.GasLimit() != 12_345_678 {
52+
t.Errorf("head gas limit mismatch: have %v, want %v", head.GasLimit(), 12_345_678)
53+
}
54+
}
55+
56+
// Tests that the simulator honors the RPC call caps set by the options.
57+
func TestWithCallGasLimitOption(t *testing.T) {
58+
// Construct a simulator, targeting a different gas limit
59+
sim := NewBackend(core.GenesisAlloc{
60+
testAddr: {Balance: big.NewInt(10000000000000000)},
61+
}, WithCallGasLimit(params.TxGas-1))
62+
defer sim.Close()
63+
64+
client := sim.Client()
65+
_, err := client.CallContract(context.Background(), ethereum.CallMsg{
66+
From: testAddr,
67+
To: &testAddr,
68+
Gas: 21000,
69+
}, nil)
70+
if !strings.Contains(err.Error(), core.ErrIntrinsicGas.Error()) {
71+
t.Fatalf("error mismatch: have %v, want %v", err, core.ErrIntrinsicGas)
72+
}
73+
}

0 commit comments

Comments
 (0)