github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/eth/catalyst/api_test.go (about) 1 // Copyright 2020 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 catalyst 18 19 import ( 20 "fmt" 21 "math/big" 22 "testing" 23 "time" 24 25 "github.com/hardtosaygoodbye/go-ethereum/common" 26 "github.com/hardtosaygoodbye/go-ethereum/consensus/ethash" 27 "github.com/hardtosaygoodbye/go-ethereum/core" 28 "github.com/hardtosaygoodbye/go-ethereum/core/rawdb" 29 "github.com/hardtosaygoodbye/go-ethereum/core/types" 30 "github.com/hardtosaygoodbye/go-ethereum/crypto" 31 "github.com/hardtosaygoodbye/go-ethereum/eth" 32 "github.com/hardtosaygoodbye/go-ethereum/eth/ethconfig" 33 "github.com/hardtosaygoodbye/go-ethereum/node" 34 "github.com/hardtosaygoodbye/go-ethereum/params" 35 ) 36 37 var ( 38 // testKey is a private key to use for funding a tester account. 39 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 40 41 // testAddr is the Ethereum address of the tester account. 42 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 43 44 testBalance = big.NewInt(2e18) 45 ) 46 47 func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { 48 db := rawdb.NewMemoryDatabase() 49 config := params.AllEthashProtocolChanges 50 genesis := &core.Genesis{ 51 Config: config, 52 Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, 53 ExtraData: []byte("test genesis"), 54 Timestamp: 9000, 55 BaseFee: big.NewInt(params.InitialBaseFee), 56 } 57 testNonce := uint64(0) 58 generate := func(i int, g *core.BlockGen) { 59 g.OffsetTime(5) 60 g.SetExtra([]byte("test")) 61 tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey) 62 g.AddTx(tx) 63 testNonce++ 64 } 65 gblock := genesis.ToBlock(db) 66 engine := ethash.NewFaker() 67 blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) 68 totalDifficulty := big.NewInt(0) 69 for _, b := range blocks { 70 totalDifficulty.Add(totalDifficulty, b.Difficulty()) 71 } 72 config.TerminalTotalDifficulty = totalDifficulty 73 return genesis, blocks 74 } 75 76 func TestEth2AssembleBlock(t *testing.T) { 77 genesis, blocks := generatePreMergeChain(10) 78 n, ethservice := startEthService(t, genesis, blocks) 79 defer n.Close() 80 81 api := NewConsensusAPI(ethservice, nil) 82 signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) 83 tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) 84 if err != nil { 85 t.Fatalf("error signing transaction, err=%v", err) 86 } 87 ethservice.TxPool().AddLocal(tx) 88 blockParams := PayloadAttributesV1{ 89 Timestamp: blocks[9].Time() + 5, 90 } 91 execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams) 92 if err != nil { 93 t.Fatalf("error producing block, err=%v", err) 94 } 95 if len(execData.Transactions) != 1 { 96 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 97 } 98 } 99 100 func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { 101 genesis, blocks := generatePreMergeChain(10) 102 n, ethservice := startEthService(t, genesis, blocks[:9]) 103 defer n.Close() 104 105 api := NewConsensusAPI(ethservice, nil) 106 107 // Put the 10th block's tx in the pool and produce a new block 108 api.insertTransactions(blocks[9].Transactions()) 109 blockParams := PayloadAttributesV1{ 110 Timestamp: blocks[8].Time() + 5, 111 } 112 execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams) 113 if err != nil { 114 t.Fatalf("error producing block, err=%v", err) 115 } 116 if len(execData.Transactions) != blocks[9].Transactions().Len() { 117 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 118 } 119 } 120 121 func TestSetHeadBeforeTotalDifficulty(t *testing.T) { 122 genesis, blocks := generatePreMergeChain(10) 123 n, ethservice := startEthService(t, genesis, blocks) 124 defer n.Close() 125 126 api := NewConsensusAPI(ethservice, nil) 127 fcState := ForkchoiceStateV1{ 128 HeadBlockHash: blocks[5].Hash(), 129 SafeBlockHash: common.Hash{}, 130 FinalizedBlockHash: common.Hash{}, 131 } 132 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err == nil { 133 t.Errorf("fork choice updated before total terminal difficulty should fail") 134 } 135 } 136 137 func TestEth2PrepareAndGetPayload(t *testing.T) { 138 genesis, blocks := generatePreMergeChain(10) 139 // We need to properly set the terminal total difficulty 140 genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty()) 141 n, ethservice := startEthService(t, genesis, blocks[:9]) 142 defer n.Close() 143 144 api := NewConsensusAPI(ethservice, nil) 145 146 // Put the 10th block's tx in the pool and produce a new block 147 api.insertTransactions(blocks[9].Transactions()) 148 blockParams := PayloadAttributesV1{ 149 Timestamp: blocks[8].Time() + 5, 150 } 151 fcState := ForkchoiceStateV1{ 152 HeadBlockHash: blocks[8].Hash(), 153 SafeBlockHash: common.Hash{}, 154 FinalizedBlockHash: common.Hash{}, 155 } 156 _, err := api.ForkchoiceUpdatedV1(fcState, &blockParams) 157 if err != nil { 158 t.Fatalf("error preparing payload, err=%v", err) 159 } 160 payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams) 161 execData, err := api.GetPayloadV1(payloadID) 162 if err != nil { 163 t.Fatalf("error getting payload, err=%v", err) 164 } 165 if len(execData.Transactions) != blocks[9].Transactions().Len() { 166 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 167 } 168 // Test invalid payloadID 169 var invPayload PayloadID 170 copy(invPayload[:], payloadID[:]) 171 invPayload[0] = ^invPayload[0] 172 _, err = api.GetPayloadV1(invPayload) 173 if err == nil { 174 t.Fatal("expected error retrieving invalid payload") 175 } 176 } 177 178 func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { 179 t.Helper() 180 181 if len(logsCh) != wantNew { 182 t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew) 183 } 184 if len(rmLogsCh) != wantRemoved { 185 t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved) 186 } 187 // Drain events. 188 for i := 0; i < len(logsCh); i++ { 189 <-logsCh 190 } 191 for i := 0; i < len(rmLogsCh); i++ { 192 <-rmLogsCh 193 } 194 } 195 196 func TestInvalidPayloadTimestamp(t *testing.T) { 197 genesis, preMergeBlocks := generatePreMergeChain(10) 198 n, ethservice := startEthService(t, genesis, preMergeBlocks) 199 ethservice.Merger().ReachTTD() 200 defer n.Close() 201 var ( 202 api = NewConsensusAPI(ethservice, nil) 203 parent = ethservice.BlockChain().CurrentBlock() 204 ) 205 tests := []struct { 206 time uint64 207 shouldErr bool 208 }{ 209 {0, true}, 210 {parent.Time(), true}, 211 {parent.Time() - 1, true}, 212 {parent.Time() + 1, false}, 213 {uint64(time.Now().Unix()) + uint64(time.Minute), false}, 214 } 215 216 for i, test := range tests { 217 t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) { 218 params := PayloadAttributesV1{ 219 Timestamp: test.time, 220 Random: crypto.Keccak256Hash([]byte{byte(123)}), 221 SuggestedFeeRecipient: parent.Coinbase(), 222 } 223 fcState := ForkchoiceStateV1{ 224 HeadBlockHash: parent.Hash(), 225 SafeBlockHash: common.Hash{}, 226 FinalizedBlockHash: common.Hash{}, 227 } 228 _, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 229 if test.shouldErr && err == nil { 230 t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err) 231 } else if !test.shouldErr && err != nil { 232 t.Fatalf("error preparing payload with valid timestamp, err=%v", err) 233 } 234 }) 235 } 236 } 237 238 func TestEth2NewBlock(t *testing.T) { 239 genesis, preMergeBlocks := generatePreMergeChain(10) 240 n, ethservice := startEthService(t, genesis, preMergeBlocks) 241 ethservice.Merger().ReachTTD() 242 defer n.Close() 243 244 var ( 245 api = NewConsensusAPI(ethservice, nil) 246 parent = preMergeBlocks[len(preMergeBlocks)-1] 247 248 // This EVM code generates a log when the contract is created. 249 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 250 ) 251 // The event channels. 252 newLogCh := make(chan []*types.Log, 10) 253 rmLogsCh := make(chan core.RemovedLogsEvent, 10) 254 ethservice.BlockChain().SubscribeLogsEvent(newLogCh) 255 ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) 256 257 for i := 0; i < 10; i++ { 258 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 259 nonce := statedb.GetNonce(testAddr) 260 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 261 ethservice.TxPool().AddLocal(tx) 262 263 execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{ 264 Timestamp: parent.Time() + 5, 265 }) 266 if err != nil { 267 t.Fatalf("Failed to create the executable data %v", err) 268 } 269 block, err := ExecutableDataToBlock(*execData) 270 if err != nil { 271 t.Fatalf("Failed to convert executable data to block %v", err) 272 } 273 newResp, err := api.ExecutePayloadV1(*execData) 274 if err != nil || newResp.Status != "VALID" { 275 t.Fatalf("Failed to insert block: %v", err) 276 } 277 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { 278 t.Fatalf("Chain head shouldn't be updated") 279 } 280 checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) 281 fcState := ForkchoiceStateV1{ 282 HeadBlockHash: block.Hash(), 283 SafeBlockHash: block.Hash(), 284 FinalizedBlockHash: block.Hash(), 285 } 286 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 287 t.Fatalf("Failed to insert block: %v", err) 288 } 289 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 290 t.Fatalf("Chain head should be updated") 291 } 292 checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) 293 294 parent = block 295 } 296 297 // Introduce fork chain 298 var ( 299 head = ethservice.BlockChain().CurrentBlock().NumberU64() 300 ) 301 parent = preMergeBlocks[len(preMergeBlocks)-1] 302 for i := 0; i < 10; i++ { 303 execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{ 304 Timestamp: parent.Time() + 6, 305 }) 306 if err != nil { 307 t.Fatalf("Failed to create the executable data %v", err) 308 } 309 block, err := ExecutableDataToBlock(*execData) 310 if err != nil { 311 t.Fatalf("Failed to convert executable data to block %v", err) 312 } 313 newResp, err := api.ExecutePayloadV1(*execData) 314 if err != nil || newResp.Status != "VALID" { 315 t.Fatalf("Failed to insert block: %v", err) 316 } 317 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 318 t.Fatalf("Chain head shouldn't be updated") 319 } 320 321 fcState := ForkchoiceStateV1{ 322 HeadBlockHash: block.Hash(), 323 SafeBlockHash: block.Hash(), 324 FinalizedBlockHash: block.Hash(), 325 } 326 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 327 t.Fatalf("Failed to insert block: %v", err) 328 } 329 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 330 t.Fatalf("Chain head should be updated") 331 } 332 parent, head = block, block.NumberU64() 333 } 334 } 335 336 func TestEth2DeepReorg(t *testing.T) { 337 // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg 338 // before the totalTerminalDifficulty threshold 339 /* 340 genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) 341 n, ethservice := startEthService(t, genesis, preMergeBlocks) 342 defer n.Close() 343 344 var ( 345 api = NewConsensusAPI(ethservice, nil) 346 parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] 347 head = ethservice.BlockChain().CurrentBlock().NumberU64() 348 ) 349 if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { 350 t.Errorf("Block %d not pruned", parent.NumberU64()) 351 } 352 for i := 0; i < 10; i++ { 353 execData, err := api.assembleBlock(AssembleBlockParams{ 354 ParentHash: parent.Hash(), 355 Timestamp: parent.Time() + 5, 356 }) 357 if err != nil { 358 t.Fatalf("Failed to create the executable data %v", err) 359 } 360 block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) 361 if err != nil { 362 t.Fatalf("Failed to convert executable data to block %v", err) 363 } 364 newResp, err := api.ExecutePayload(*execData) 365 if err != nil || newResp.Status != "VALID" { 366 t.Fatalf("Failed to insert block: %v", err) 367 } 368 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 369 t.Fatalf("Chain head shouldn't be updated") 370 } 371 if err := api.setHead(block.Hash()); err != nil { 372 t.Fatalf("Failed to set head: %v", err) 373 } 374 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 375 t.Fatalf("Chain head should be updated") 376 } 377 parent, head = block, block.NumberU64() 378 } 379 */ 380 } 381 382 // startEthService creates a full node instance for testing. 383 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { 384 t.Helper() 385 386 n, err := node.New(&node.Config{}) 387 if err != nil { 388 t.Fatal("can't create node:", err) 389 } 390 391 ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} 392 ethservice, err := eth.New(n, ethcfg) 393 if err != nil { 394 t.Fatal("can't create eth service:", err) 395 } 396 if err := n.Start(); err != nil { 397 t.Fatal("can't start node:", err) 398 } 399 if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { 400 n.Close() 401 t.Fatal("can't import test blocks:", err) 402 } 403 ethservice.SetEtherbase(testAddr) 404 ethservice.SetSynced() 405 406 return n, ethservice 407 } 408 409 func TestFullAPI(t *testing.T) { 410 genesis, preMergeBlocks := generatePreMergeChain(10) 411 n, ethservice := startEthService(t, genesis, preMergeBlocks) 412 ethservice.Merger().ReachTTD() 413 defer n.Close() 414 var ( 415 api = NewConsensusAPI(ethservice, nil) 416 parent = ethservice.BlockChain().CurrentBlock() 417 // This EVM code generates a log when the contract is created. 418 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 419 ) 420 for i := 0; i < 10; i++ { 421 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 422 nonce := statedb.GetNonce(testAddr) 423 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 424 ethservice.TxPool().AddLocal(tx) 425 426 params := PayloadAttributesV1{ 427 Timestamp: parent.Time() + 1, 428 Random: crypto.Keccak256Hash([]byte{byte(i)}), 429 SuggestedFeeRecipient: parent.Coinbase(), 430 } 431 fcState := ForkchoiceStateV1{ 432 HeadBlockHash: parent.Hash(), 433 SafeBlockHash: common.Hash{}, 434 FinalizedBlockHash: common.Hash{}, 435 } 436 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 437 if err != nil { 438 t.Fatalf("error preparing payload, err=%v", err) 439 } 440 if resp.Status != SUCCESS.Status { 441 t.Fatalf("error preparing payload, invalid status: %v", resp.Status) 442 } 443 payloadID := computePayloadId(parent.Hash(), ¶ms) 444 payload, err := api.GetPayloadV1(payloadID) 445 if err != nil { 446 t.Fatalf("can't get payload: %v", err) 447 } 448 execResp, err := api.ExecutePayloadV1(*payload) 449 if err != nil { 450 t.Fatalf("can't execute payload: %v", err) 451 } 452 if execResp.Status != VALID.Status { 453 t.Fatalf("invalid status: %v", execResp.Status) 454 } 455 fcState = ForkchoiceStateV1{ 456 HeadBlockHash: payload.BlockHash, 457 SafeBlockHash: payload.ParentHash, 458 FinalizedBlockHash: payload.ParentHash, 459 } 460 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 461 t.Fatalf("Failed to insert block: %v", err) 462 } 463 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 464 t.Fatalf("Chain head should be updated") 465 } 466 parent = ethservice.BlockChain().CurrentBlock() 467 } 468 }