github.com/ethereum/go-ethereum@v1.16.1/ethclient/simulated/backend.go (about) 1 // Copyright 2023 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 "errors" 21 "time" 22 23 "github.com/ethereum/go-ethereum" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/eth" 28 "github.com/ethereum/go-ethereum/eth/catalyst" 29 "github.com/ethereum/go-ethereum/eth/ethconfig" 30 "github.com/ethereum/go-ethereum/eth/filters" 31 "github.com/ethereum/go-ethereum/ethclient" 32 "github.com/ethereum/go-ethereum/node" 33 "github.com/ethereum/go-ethereum/p2p" 34 "github.com/ethereum/go-ethereum/params" 35 "github.com/ethereum/go-ethereum/rpc" 36 ) 37 38 // Client exposes the methods provided by the Ethereum RPC client. 39 type Client interface { 40 ethereum.BlockNumberReader 41 ethereum.ChainReader 42 ethereum.ChainStateReader 43 ethereum.ContractCaller 44 ethereum.GasEstimator 45 ethereum.GasPricer 46 ethereum.GasPricer1559 47 ethereum.FeeHistoryReader 48 ethereum.LogFilterer 49 ethereum.PendingStateReader 50 ethereum.PendingContractCaller 51 ethereum.TransactionReader 52 ethereum.TransactionSender 53 ethereum.ChainIDReader 54 } 55 56 // simClient wraps ethclient. This exists to prevent extracting ethclient.Client 57 // from the Client interface returned by Backend. 58 type simClient struct { 59 *ethclient.Client 60 } 61 62 // Backend is a simulated blockchain. You can use it to test your contracts or 63 // other code that interacts with the Ethereum chain. 64 type Backend struct { 65 node *node.Node 66 beacon *catalyst.SimulatedBeacon 67 client simClient 68 } 69 70 // NewBackend creates a new simulated blockchain that can be used as a backend for 71 // contract bindings in unit tests. 72 // 73 // A simulated backend always uses chainID 1337. 74 func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config, ethConf *ethconfig.Config)) *Backend { 75 // Create the default configurations for the outer node shell and the Ethereum 76 // service to mutate with the options afterwards 77 nodeConf := node.DefaultConfig 78 nodeConf.DataDir = "" 79 nodeConf.P2P = p2p.Config{NoDiscovery: true} 80 81 ethConf := ethconfig.Defaults 82 ethConf.Genesis = &core.Genesis{ 83 Config: params.AllDevChainProtocolChanges, 84 GasLimit: ethconfig.Defaults.Miner.GasCeil, 85 Alloc: alloc, 86 } 87 ethConf.SyncMode = ethconfig.FullSync 88 ethConf.TxPool.NoLocals = true 89 90 for _, option := range options { 91 option(&nodeConf, ðConf) 92 } 93 // Assemble the Ethereum stack to run the chain with 94 stack, err := node.New(&nodeConf) 95 if err != nil { 96 panic(err) // this should never happen 97 } 98 sim, err := newWithNode(stack, ðConf, 0) 99 if err != nil { 100 panic(err) // this should never happen 101 } 102 return sim 103 } 104 105 // newWithNode sets up a simulated backend on an existing node. The provided node 106 // must not be started and will be started by this method. 107 func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) { 108 backend, err := eth.New(stack, conf) 109 if err != nil { 110 return nil, err 111 } 112 // Register the filter system 113 filterSystem := filters.NewFilterSystem(backend.APIBackend, filters.Config{}) 114 stack.RegisterAPIs([]rpc.API{{ 115 Namespace: "eth", 116 Service: filters.NewFilterAPI(filterSystem), 117 }}) 118 // Start the node 119 if err := stack.Start(); err != nil { 120 return nil, err 121 } 122 // Set up the simulated beacon 123 beacon, err := catalyst.NewSimulatedBeacon(blockPeriod, common.Address{}, backend) 124 if err != nil { 125 return nil, err 126 } 127 // Reorg our chain back to genesis 128 if err := beacon.Fork(backend.BlockChain().GetCanonicalHash(0)); err != nil { 129 return nil, err 130 } 131 return &Backend{ 132 node: stack, 133 beacon: beacon, 134 client: simClient{ethclient.NewClient(stack.Attach())}, 135 }, nil 136 } 137 138 // Close shuts down the simBackend. 139 // The simulated backend can't be used afterwards. 140 func (n *Backend) Close() error { 141 if n.client.Client != nil { 142 n.client.Close() 143 n.client = simClient{} 144 } 145 var err error 146 if n.beacon != nil { 147 err = n.beacon.Stop() 148 n.beacon = nil 149 } 150 if n.node != nil { 151 err = errors.Join(err, n.node.Close()) 152 n.node = nil 153 } 154 return err 155 } 156 157 // Commit seals a block and moves the chain forward to a new empty block. 158 func (n *Backend) Commit() common.Hash { 159 return n.beacon.Commit() 160 } 161 162 // Rollback removes all pending transactions, reverting to the last committed state. 163 func (n *Backend) Rollback() { 164 n.beacon.Rollback() 165 } 166 167 // Fork creates a side-chain that can be used to simulate reorgs. 168 // 169 // This function should be called with the ancestor block where the new side 170 // chain should be started. Transactions (old and new) can then be applied on 171 // top and Commit-ed. 172 // 173 // Note, the side-chain will only become canonical (and trigger the events) when 174 // it becomes longer. Until then CallContract will still operate on the current 175 // canonical chain. 176 // 177 // There is a % chance that the side chain becomes canonical at the same length 178 // to simulate live network behavior. 179 func (n *Backend) Fork(parentHash common.Hash) error { 180 return n.beacon.Fork(parentHash) 181 } 182 183 // AdjustTime changes the block timestamp and creates a new block. 184 // It can only be called on empty blocks. 185 func (n *Backend) AdjustTime(adjustment time.Duration) error { 186 return n.beacon.AdjustTime(adjustment) 187 } 188 189 // Client returns a client that accesses the simulated chain. 190 func (n *Backend) Client() Client { 191 return n.client 192 }