github.com/ethereum/go-ethereum@v1.16.1/ethclient/simulated/backend_test.go (about) 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 "crypto/ecdsa" 22 "crypto/sha256" 23 "math/big" 24 "math/rand" 25 "testing" 26 "time" 27 28 "github.com/ethereum/go-ethereum/accounts/abi/bind" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/core/types" 31 "github.com/ethereum/go-ethereum/crypto" 32 "github.com/ethereum/go-ethereum/crypto/kzg4844" 33 "github.com/ethereum/go-ethereum/params" 34 "github.com/holiman/uint256" 35 "go.uber.org/goleak" 36 ) 37 38 var _ bind.ContractBackend = (Client)(nil) 39 40 var ( 41 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 42 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 43 testKey2, _ = crypto.HexToECDSA("7ee346e3f7efc685250053bfbafbfc880d58dc6145247053d4fb3cb0f66dfcb2") 44 testAddr2 = crypto.PubkeyToAddress(testKey2.PublicKey) 45 ) 46 47 func simTestBackend(testAddr common.Address) *Backend { 48 return NewBackend( 49 types.GenesisAlloc{ 50 testAddr: {Balance: big.NewInt(10000000000000000)}, 51 }, 52 ) 53 } 54 55 func newBlobTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { 56 client := sim.Client() 57 58 testBlob := &kzg4844.Blob{0x00} 59 testBlobCommit, _ := kzg4844.BlobToCommitment(testBlob) 60 testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit) 61 testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit) 62 63 head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough 64 gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei)) 65 gasPriceU256, _ := uint256.FromBig(gasPrice) 66 gasTipCapU256, _ := uint256.FromBig(big.NewInt(params.GWei)) 67 68 addr := crypto.PubkeyToAddress(key.PublicKey) 69 chainid, _ := client.ChainID(context.Background()) 70 nonce, err := client.PendingNonceAt(context.Background(), addr) 71 if err != nil { 72 return nil, err 73 } 74 75 chainidU256, _ := uint256.FromBig(chainid) 76 tx := types.NewTx(&types.BlobTx{ 77 ChainID: chainidU256, 78 GasTipCap: gasTipCapU256, 79 GasFeeCap: gasPriceU256, 80 BlobFeeCap: uint256.NewInt(1), 81 Gas: 21000, 82 Nonce: nonce, 83 To: addr, 84 AccessList: nil, 85 BlobHashes: []common.Hash{testBlobVHash}, 86 Sidecar: &types.BlobTxSidecar{ 87 Blobs: []kzg4844.Blob{*testBlob}, 88 Commitments: []kzg4844.Commitment{testBlobCommit}, 89 Proofs: []kzg4844.Proof{testBlobProof}, 90 }, 91 }) 92 return types.SignTx(tx, types.LatestSignerForChainID(chainid), key) 93 } 94 95 func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { 96 client := sim.Client() 97 98 // create a signed transaction to send 99 head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough 100 gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei)) 101 addr := crypto.PubkeyToAddress(key.PublicKey) 102 chainid, _ := client.ChainID(context.Background()) 103 nonce, err := client.PendingNonceAt(context.Background(), addr) 104 if err != nil { 105 return nil, err 106 } 107 tx := types.NewTx(&types.DynamicFeeTx{ 108 ChainID: chainid, 109 Nonce: nonce, 110 GasTipCap: big.NewInt(params.GWei), 111 GasFeeCap: gasPrice, 112 Gas: 21000, 113 To: &addr, 114 }) 115 116 return types.SignTx(tx, types.LatestSignerForChainID(chainid), key) 117 } 118 119 func TestNewBackend(t *testing.T) { 120 sim := NewBackend(types.GenesisAlloc{}) 121 defer sim.Close() 122 123 client := sim.Client() 124 num, err := client.BlockNumber(context.Background()) 125 if err != nil { 126 t.Fatal(err) 127 } 128 if num != 0 { 129 t.Fatalf("expected 0 got %v", num) 130 } 131 // Create a block 132 sim.Commit() 133 num, err = client.BlockNumber(context.Background()) 134 if err != nil { 135 t.Fatal(err) 136 } 137 if num != 1 { 138 t.Fatalf("expected 1 got %v", num) 139 } 140 } 141 142 func TestAdjustTime(t *testing.T) { 143 sim := NewBackend(types.GenesisAlloc{}) 144 defer sim.Close() 145 146 client := sim.Client() 147 block1, _ := client.BlockByNumber(context.Background(), nil) 148 149 // Create a block 150 if err := sim.AdjustTime(time.Minute); err != nil { 151 t.Fatal(err) 152 } 153 block2, _ := client.BlockByNumber(context.Background(), nil) 154 prevTime := block1.Time() 155 newTime := block2.Time() 156 if newTime-prevTime != 60 { 157 t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime) 158 } 159 } 160 161 func TestSendTransaction(t *testing.T) { 162 sim := simTestBackend(testAddr) 163 defer sim.Close() 164 165 client := sim.Client() 166 ctx := context.Background() 167 168 signedTx, err := newTx(sim, testKey) 169 if err != nil { 170 t.Errorf("could not create transaction: %v", err) 171 } 172 // send tx to simulated backend 173 err = client.SendTransaction(ctx, signedTx) 174 if err != nil { 175 t.Errorf("could not add tx to pending block: %v", err) 176 } 177 sim.Commit() 178 block, err := client.BlockByNumber(ctx, big.NewInt(1)) 179 if err != nil { 180 t.Errorf("could not get block at height 1: %v", err) 181 } 182 183 if signedTx.Hash() != block.Transactions()[0].Hash() { 184 t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash()) 185 } 186 } 187 188 // TestFork check that the chain length after a reorg is correct. 189 // Steps: 190 // 1. Save the current block which will serve as parent for the fork. 191 // 2. Mine n blocks with n ∈ [0, 20]. 192 // 3. Assert that the chain length is n. 193 // 4. Fork by using the parent block as ancestor. 194 // 5. Mine n+1 blocks which should trigger a reorg. 195 // 6. Assert that the chain length is n+1. 196 // Since Commit() was called 2n+1 times in total, 197 // having a chain length of just n+1 means that a reorg occurred. 198 func TestFork(t *testing.T) { 199 t.Parallel() 200 testAddr := crypto.PubkeyToAddress(testKey.PublicKey) 201 sim := simTestBackend(testAddr) 202 defer sim.Close() 203 204 client := sim.Client() 205 ctx := context.Background() 206 207 // 1. 208 parent, _ := client.HeaderByNumber(ctx, nil) 209 210 // 2. 211 n := int(rand.Int31n(21)) 212 for i := 0; i < n; i++ { 213 sim.Commit() 214 } 215 216 // 3. 217 b, _ := client.BlockNumber(ctx) 218 if b != uint64(n) { 219 t.Error("wrong chain length") 220 } 221 222 // 4. 223 sim.Fork(parent.Hash()) 224 225 // 5. 226 for i := 0; i < n+1; i++ { 227 sim.Commit() 228 } 229 230 // 6. 231 b, _ = client.BlockNumber(ctx) 232 if b != uint64(n+1) { 233 t.Error("wrong chain length") 234 } 235 } 236 237 // TestForkResendTx checks that re-sending a TX after a fork 238 // is possible and does not cause a "nonce mismatch" panic. 239 // Steps: 240 // 1. Save the current block which will serve as parent for the fork. 241 // 2. Send a transaction. 242 // 3. Check that the TX is included in block 1. 243 // 4. Fork by using the parent block as ancestor. 244 // 5. Mine a block. We expect the out-forked tx to have trickled to the pool, and into the new block. 245 // 6. Check that the TX is now included in (the new) block 1. 246 func TestForkResendTx(t *testing.T) { 247 t.Parallel() 248 testAddr := crypto.PubkeyToAddress(testKey.PublicKey) 249 sim := simTestBackend(testAddr) 250 defer sim.Close() 251 252 client := sim.Client() 253 ctx := context.Background() 254 255 // 1. 256 parent, _ := client.HeaderByNumber(ctx, nil) 257 258 // 2. 259 tx, err := newTx(sim, testKey) 260 if err != nil { 261 t.Fatalf("could not create transaction: %v", err) 262 } 263 if err := client.SendTransaction(ctx, tx); err != nil { 264 t.Fatalf("sending transaction: %v", err) 265 } 266 sim.Commit() 267 268 // 3. 269 receipt, _ := client.TransactionReceipt(ctx, tx.Hash()) 270 if h := receipt.BlockNumber.Uint64(); h != 1 { 271 t.Errorf("TX included in wrong block: %d", h) 272 } 273 274 // 4. 275 if err := sim.Fork(parent.Hash()); err != nil { 276 t.Errorf("forking: %v", err) 277 } 278 279 // 5. 280 sim.Commit() 281 receipt, _ = client.TransactionReceipt(ctx, tx.Hash()) 282 if h := receipt.BlockNumber.Uint64(); h != 1 { 283 t.Errorf("TX included in wrong block: %d", h) 284 } 285 } 286 287 func TestCommitReturnValue(t *testing.T) { 288 t.Parallel() 289 testAddr := crypto.PubkeyToAddress(testKey.PublicKey) 290 sim := simTestBackend(testAddr) 291 defer sim.Close() 292 293 client := sim.Client() 294 ctx := context.Background() 295 296 // Test if Commit returns the correct block hash 297 h1 := sim.Commit() 298 cur, _ := client.HeaderByNumber(ctx, nil) 299 if h1 != cur.Hash() { 300 t.Error("Commit did not return the hash of the last block.") 301 } 302 303 // Create a block in the original chain (containing a transaction to force different block hashes) 304 tx, _ := newTx(sim, testKey) 305 if err := client.SendTransaction(ctx, tx); err != nil { 306 t.Errorf("sending transaction: %v", err) 307 } 308 309 h2 := sim.Commit() 310 311 // Create another block in the original chain 312 sim.Commit() 313 314 // Fork at the first bock 315 if err := sim.Fork(h1); err != nil { 316 t.Errorf("forking: %v", err) 317 } 318 319 // Test if Commit returns the correct block hash after the reorg 320 h2fork := sim.Commit() 321 if h2 == h2fork { 322 t.Error("The block in the fork and the original block are the same block!") 323 } 324 if header, err := client.HeaderByHash(ctx, h2fork); err != nil || header == nil { 325 t.Error("Could not retrieve the just created block (side-chain)") 326 } 327 } 328 329 // TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork 330 // block's parent rather than the canonical head's parent. 331 func TestAdjustTimeAfterFork(t *testing.T) { 332 t.Parallel() 333 testAddr := crypto.PubkeyToAddress(testKey.PublicKey) 334 sim := simTestBackend(testAddr) 335 defer sim.Close() 336 337 client := sim.Client() 338 ctx := context.Background() 339 340 sim.Commit() // h1 341 h1, _ := client.HeaderByNumber(ctx, nil) 342 343 sim.Commit() // h2 344 sim.Fork(h1.Hash()) 345 sim.AdjustTime(1 * time.Second) 346 sim.Commit() 347 348 head, _ := client.HeaderByNumber(ctx, nil) 349 if head.Number.Uint64() == 2 && head.ParentHash != h1.Hash() { 350 t.Errorf("failed to build block on fork") 351 } 352 } 353 354 func createAndCloseSimBackend() { 355 genesisData := types.GenesisAlloc{} 356 simulatedBackend := NewBackend(genesisData) 357 defer simulatedBackend.Close() 358 } 359 360 // TestCheckSimBackendGoroutineLeak checks whether creation of a simulated backend leaks go-routines. Any long-lived go-routines 361 // spawned by global variables are not considered leaked. 362 func TestCheckSimBackendGoroutineLeak(t *testing.T) { 363 createAndCloseSimBackend() 364 ignoreCur := goleak.IgnoreCurrent() 365 // ignore this leveldb function: this go-routine is guaranteed to be terminated 1 second after closing db handle 366 ignoreLdb := goleak.IgnoreAnyFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain") 367 createAndCloseSimBackend() 368 goleak.VerifyNone(t, ignoreCur, ignoreLdb) 369 }