github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/eth/catalyst/api_test.go (about) 1 // Copyright 2021 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 "bytes" 21 "fmt" 22 "math/big" 23 "testing" 24 "time" 25 26 "github.com/electroneum/electroneum-sc/common" 27 "github.com/electroneum/electroneum-sc/common/hexutil" 28 "github.com/electroneum/electroneum-sc/consensus/ethash" 29 "github.com/electroneum/electroneum-sc/core" 30 "github.com/electroneum/electroneum-sc/core/beacon" 31 "github.com/electroneum/electroneum-sc/core/rawdb" 32 "github.com/electroneum/electroneum-sc/core/types" 33 "github.com/electroneum/electroneum-sc/crypto" 34 "github.com/electroneum/electroneum-sc/eth" 35 "github.com/electroneum/electroneum-sc/eth/downloader" 36 "github.com/electroneum/electroneum-sc/eth/ethconfig" 37 "github.com/electroneum/electroneum-sc/node" 38 "github.com/electroneum/electroneum-sc/p2p" 39 "github.com/electroneum/electroneum-sc/params" 40 "github.com/electroneum/electroneum-sc/trie" 41 ) 42 43 var ( 44 // testKey is a private key to use for funding a tester account. 45 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 46 47 // testAddr is the Ethereum address of the tester account. 48 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 49 50 testBalance = big.NewInt(2e18) 51 ) 52 53 func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { 54 db := rawdb.NewMemoryDatabase() 55 config := params.AllEthashProtocolChanges 56 genesis := &core.Genesis{ 57 Config: config, 58 Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, 59 ExtraData: []byte("test genesis"), 60 Timestamp: 9000, 61 BaseFee: big.NewInt(params.InitialBaseFee), 62 Difficulty: big.NewInt(0), 63 } 64 testNonce := uint64(0) 65 generate := func(i int, g *core.BlockGen) { 66 g.OffsetTime(5) 67 g.SetExtra([]byte("test")) 68 tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey) 69 g.AddTx(tx) 70 testNonce++ 71 } 72 gblock := genesis.ToBlock(db) 73 engine := ethash.NewFaker() 74 blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) 75 totalDifficulty := big.NewInt(0) 76 for _, b := range blocks { 77 totalDifficulty.Add(totalDifficulty, b.Difficulty()) 78 } 79 config.TerminalTotalDifficulty = totalDifficulty 80 return genesis, blocks 81 } 82 83 func TestEth2AssembleBlock(t *testing.T) { 84 genesis, blocks := generatePreMergeChain(10) 85 n, ethservice := startEthService(t, genesis, blocks) 86 defer n.Close() 87 88 api := NewConsensusAPI(ethservice) 89 signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) 90 tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) 91 if err != nil { 92 t.Fatalf("error signing transaction, err=%v", err) 93 } 94 ethservice.TxPool().AddLocal(tx) 95 blockParams := beacon.PayloadAttributesV1{ 96 Timestamp: blocks[9].Time() + 5, 97 } 98 execData, err := assembleBlock(api, blocks[9].Hash(), &blockParams) 99 if err != nil { 100 t.Fatalf("error producing block, err=%v", err) 101 } 102 if len(execData.Transactions) != 1 { 103 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 104 } 105 } 106 107 func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { 108 genesis, blocks := generatePreMergeChain(10) 109 n, ethservice := startEthService(t, genesis, blocks[:9]) 110 defer n.Close() 111 112 api := NewConsensusAPI(ethservice) 113 114 // Put the 10th block's tx in the pool and produce a new block 115 api.eth.TxPool().AddRemotesSync(blocks[9].Transactions()) 116 blockParams := beacon.PayloadAttributesV1{ 117 Timestamp: blocks[8].Time() + 5, 118 } 119 execData, err := assembleBlock(api, blocks[8].Hash(), &blockParams) 120 if err != nil { 121 t.Fatalf("error producing block, err=%v", err) 122 } 123 if len(execData.Transactions) != blocks[9].Transactions().Len() { 124 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 125 } 126 } 127 128 func TestSetHeadBeforeTotalDifficulty(t *testing.T) { 129 genesis, blocks := generatePreMergeChain(10) 130 n, ethservice := startEthService(t, genesis, blocks) 131 defer n.Close() 132 133 api := NewConsensusAPI(ethservice) 134 fcState := beacon.ForkchoiceStateV1{ 135 HeadBlockHash: blocks[5].Hash(), 136 SafeBlockHash: common.Hash{}, 137 FinalizedBlockHash: common.Hash{}, 138 } 139 if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 140 t.Errorf("fork choice updated should not error: %v", err) 141 } else if resp.PayloadStatus.Status != beacon.INVALID_TERMINAL_BLOCK.Status { 142 t.Errorf("fork choice updated before total terminal difficulty should be INVALID") 143 } 144 } 145 146 func TestEth2PrepareAndGetPayload(t *testing.T) { 147 genesis, blocks := generatePreMergeChain(10) 148 // We need to properly set the terminal total difficulty 149 genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty()) 150 n, ethservice := startEthService(t, genesis, blocks[:9]) 151 defer n.Close() 152 153 api := NewConsensusAPI(ethservice) 154 155 // Put the 10th block's tx in the pool and produce a new block 156 ethservice.TxPool().AddLocals(blocks[9].Transactions()) 157 blockParams := beacon.PayloadAttributesV1{ 158 Timestamp: blocks[8].Time() + 5, 159 } 160 fcState := beacon.ForkchoiceStateV1{ 161 HeadBlockHash: blocks[8].Hash(), 162 SafeBlockHash: common.Hash{}, 163 FinalizedBlockHash: common.Hash{}, 164 } 165 _, err := api.ForkchoiceUpdatedV1(fcState, &blockParams) 166 if err != nil { 167 t.Fatalf("error preparing payload, err=%v", err) 168 } 169 payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams) 170 execData, err := api.GetPayloadV1(payloadID) 171 if err != nil { 172 t.Fatalf("error getting payload, err=%v", err) 173 } 174 if len(execData.Transactions) != blocks[9].Transactions().Len() { 175 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 176 } 177 // Test invalid payloadID 178 var invPayload beacon.PayloadID 179 copy(invPayload[:], payloadID[:]) 180 invPayload[0] = ^invPayload[0] 181 _, err = api.GetPayloadV1(invPayload) 182 if err == nil { 183 t.Fatal("expected error retrieving invalid payload") 184 } 185 } 186 187 func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { 188 t.Helper() 189 190 if len(logsCh) != wantNew { 191 t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew) 192 } 193 if len(rmLogsCh) != wantRemoved { 194 t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved) 195 } 196 // Drain events. 197 for i := 0; i < len(logsCh); i++ { 198 <-logsCh 199 } 200 for i := 0; i < len(rmLogsCh); i++ { 201 <-rmLogsCh 202 } 203 } 204 205 func TestInvalidPayloadTimestamp(t *testing.T) { 206 genesis, preMergeBlocks := generatePreMergeChain(10) 207 n, ethservice := startEthService(t, genesis, preMergeBlocks) 208 ethservice.Merger().ReachTTD() 209 defer n.Close() 210 var ( 211 api = NewConsensusAPI(ethservice) 212 parent = ethservice.BlockChain().CurrentBlock() 213 ) 214 tests := []struct { 215 time uint64 216 shouldErr bool 217 }{ 218 {0, true}, 219 {parent.Time(), true}, 220 {parent.Time() - 1, true}, 221 222 // TODO (MariusVanDerWijden) following tests are currently broken, 223 // fixed in upcoming merge-kiln-v2 pr 224 //{parent.Time() + 1, false}, 225 //{uint64(time.Now().Unix()) + uint64(time.Minute), false}, 226 } 227 228 for i, test := range tests { 229 t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) { 230 params := beacon.PayloadAttributesV1{ 231 Timestamp: test.time, 232 Random: crypto.Keccak256Hash([]byte{byte(123)}), 233 SuggestedFeeRecipient: parent.Coinbase(), 234 } 235 fcState := beacon.ForkchoiceStateV1{ 236 HeadBlockHash: parent.Hash(), 237 SafeBlockHash: common.Hash{}, 238 FinalizedBlockHash: common.Hash{}, 239 } 240 _, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 241 if test.shouldErr && err == nil { 242 t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err) 243 } else if !test.shouldErr && err != nil { 244 t.Fatalf("error preparing payload with valid timestamp, err=%v", err) 245 } 246 }) 247 } 248 } 249 250 func TestEth2NewBlock(t *testing.T) { 251 genesis, preMergeBlocks := generatePreMergeChain(10) 252 n, ethservice := startEthService(t, genesis, preMergeBlocks) 253 ethservice.Merger().ReachTTD() 254 defer n.Close() 255 256 var ( 257 api = NewConsensusAPI(ethservice) 258 parent = preMergeBlocks[len(preMergeBlocks)-1] 259 260 // This EVM code generates a log when the contract is created. 261 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 262 ) 263 // The event channels. 264 newLogCh := make(chan []*types.Log, 10) 265 rmLogsCh := make(chan core.RemovedLogsEvent, 10) 266 ethservice.BlockChain().SubscribeLogsEvent(newLogCh) 267 ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) 268 269 for i := 0; i < 10; i++ { 270 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 271 nonce := statedb.GetNonce(testAddr) 272 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 273 ethservice.TxPool().AddLocal(tx) 274 275 execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{ 276 Timestamp: parent.Time() + 5, 277 }) 278 if err != nil { 279 t.Fatalf("Failed to create the executable data %v", err) 280 } 281 block, err := beacon.ExecutableDataToBlock(*execData) 282 if err != nil { 283 t.Fatalf("Failed to convert executable data to block %v", err) 284 } 285 newResp, err := api.NewPayloadV1(*execData) 286 if err != nil || newResp.Status != "VALID" { 287 t.Fatalf("Failed to insert block: %v", err) 288 } 289 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { 290 t.Fatalf("Chain head shouldn't be updated") 291 } 292 checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) 293 fcState := beacon.ForkchoiceStateV1{ 294 HeadBlockHash: block.Hash(), 295 SafeBlockHash: block.Hash(), 296 FinalizedBlockHash: block.Hash(), 297 } 298 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 299 t.Fatalf("Failed to insert block: %v", err) 300 } 301 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 302 t.Fatalf("Chain head should be updated") 303 } 304 checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) 305 306 parent = block 307 } 308 309 // Introduce fork chain 310 var ( 311 head = ethservice.BlockChain().CurrentBlock().NumberU64() 312 ) 313 parent = preMergeBlocks[len(preMergeBlocks)-1] 314 for i := 0; i < 10; i++ { 315 execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{ 316 Timestamp: parent.Time() + 6, 317 }) 318 if err != nil { 319 t.Fatalf("Failed to create the executable data %v", err) 320 } 321 block, err := beacon.ExecutableDataToBlock(*execData) 322 if err != nil { 323 t.Fatalf("Failed to convert executable data to block %v", err) 324 } 325 newResp, err := api.NewPayloadV1(*execData) 326 if err != nil || newResp.Status != "VALID" { 327 t.Fatalf("Failed to insert block: %v", err) 328 } 329 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 330 t.Fatalf("Chain head shouldn't be updated") 331 } 332 333 fcState := beacon.ForkchoiceStateV1{ 334 HeadBlockHash: block.Hash(), 335 SafeBlockHash: block.Hash(), 336 FinalizedBlockHash: block.Hash(), 337 } 338 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 339 t.Fatalf("Failed to insert block: %v", err) 340 } 341 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 342 t.Fatalf("Chain head should be updated") 343 } 344 parent, head = block, block.NumberU64() 345 } 346 } 347 348 func TestEth2DeepReorg(t *testing.T) { 349 // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg 350 // before the totalTerminalDifficulty threshold 351 /* 352 genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) 353 n, ethservice := startEthService(t, genesis, preMergeBlocks) 354 defer n.Close() 355 356 var ( 357 api = NewConsensusAPI(ethservice, nil) 358 parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] 359 head = ethservice.BlockChain().CurrentBlock().NumberU64() 360 ) 361 if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { 362 t.Errorf("Block %d not pruned", parent.NumberU64()) 363 } 364 for i := 0; i < 10; i++ { 365 execData, err := api.assembleBlock(AssembleBlockParams{ 366 ParentHash: parent.Hash(), 367 Timestamp: parent.Time() + 5, 368 }) 369 if err != nil { 370 t.Fatalf("Failed to create the executable data %v", err) 371 } 372 block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) 373 if err != nil { 374 t.Fatalf("Failed to convert executable data to block %v", err) 375 } 376 newResp, err := api.ExecutePayload(*execData) 377 if err != nil || newResp.Status != "VALID" { 378 t.Fatalf("Failed to insert block: %v", err) 379 } 380 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 381 t.Fatalf("Chain head shouldn't be updated") 382 } 383 if err := api.setHead(block.Hash()); err != nil { 384 t.Fatalf("Failed to set head: %v", err) 385 } 386 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 387 t.Fatalf("Chain head should be updated") 388 } 389 parent, head = block, block.NumberU64() 390 } 391 */ 392 } 393 394 // startEthService creates a full node instance for testing. 395 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { 396 t.Helper() 397 398 n, err := node.New(&node.Config{ 399 P2P: p2p.Config{ 400 ListenAddr: "0.0.0.0:0", 401 NoDiscovery: true, 402 MaxPeers: 25, 403 }}) 404 if err != nil { 405 t.Fatal("can't create node:", err) 406 } 407 408 ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.SnapSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} 409 ethservice, err := eth.New(n, ethcfg) 410 if err != nil { 411 t.Fatal("can't create eth service:", err) 412 } 413 if err := n.Start(); err != nil { 414 t.Fatal("can't start node:", err) 415 } 416 if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { 417 n.Close() 418 t.Fatal("can't import test blocks:", err) 419 } 420 time.Sleep(500 * time.Millisecond) // give txpool enough time to consume head event 421 422 ethservice.SetEtherbase(testAddr) 423 ethservice.SetSynced() 424 return n, ethservice 425 } 426 427 func TestFullAPI(t *testing.T) { 428 genesis, preMergeBlocks := generatePreMergeChain(10) 429 n, ethservice := startEthService(t, genesis, preMergeBlocks) 430 ethservice.Merger().ReachTTD() 431 defer n.Close() 432 var ( 433 parent = ethservice.BlockChain().CurrentBlock() 434 // This EVM code generates a log when the contract is created. 435 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 436 ) 437 438 callback := func(parent *types.Block) { 439 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 440 nonce := statedb.GetNonce(testAddr) 441 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 442 ethservice.TxPool().AddLocal(tx) 443 } 444 445 setupBlocks(t, ethservice, 10, parent, callback) 446 } 447 448 func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) { 449 api := NewConsensusAPI(ethservice) 450 for i := 0; i < n; i++ { 451 callback(parent) 452 453 payload := getNewPayload(t, api, parent) 454 455 execResp, err := api.NewPayloadV1(*payload) 456 if err != nil { 457 t.Fatalf("can't execute payload: %v", err) 458 } 459 if execResp.Status != beacon.VALID { 460 t.Fatalf("invalid status: %v", execResp.Status) 461 } 462 fcState := beacon.ForkchoiceStateV1{ 463 HeadBlockHash: payload.BlockHash, 464 SafeBlockHash: payload.ParentHash, 465 FinalizedBlockHash: payload.ParentHash, 466 } 467 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 468 t.Fatalf("Failed to insert block: %v", err) 469 } 470 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 471 t.Fatal("Chain head should be updated") 472 } 473 if ethservice.BlockChain().CurrentFinalizedBlock().NumberU64() != payload.Number-1 { 474 t.Fatal("Finalized block should be updated") 475 } 476 parent = ethservice.BlockChain().CurrentBlock() 477 } 478 } 479 480 func TestExchangeTransitionConfig(t *testing.T) { 481 genesis, preMergeBlocks := generatePreMergeChain(10) 482 n, ethservice := startEthService(t, genesis, preMergeBlocks) 483 ethservice.Merger().ReachTTD() 484 defer n.Close() 485 var ( 486 api = NewConsensusAPI(ethservice) 487 ) 488 // invalid ttd 489 config := beacon.TransitionConfigurationV1{ 490 TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)), 491 TerminalBlockHash: common.Hash{}, 492 TerminalBlockNumber: 0, 493 } 494 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 495 t.Fatal("expected error on invalid config, invalid ttd") 496 } 497 // invalid terminal block hash 498 config = beacon.TransitionConfigurationV1{ 499 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 500 TerminalBlockHash: common.Hash{1}, 501 TerminalBlockNumber: 0, 502 } 503 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 504 t.Fatal("expected error on invalid config, invalid hash") 505 } 506 // valid config 507 config = beacon.TransitionConfigurationV1{ 508 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 509 TerminalBlockHash: common.Hash{}, 510 TerminalBlockNumber: 0, 511 } 512 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 513 t.Fatalf("expected no error on valid config, got %v", err) 514 } 515 // valid config 516 config = beacon.TransitionConfigurationV1{ 517 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 518 TerminalBlockHash: preMergeBlocks[5].Hash(), 519 TerminalBlockNumber: 6, 520 } 521 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 522 t.Fatalf("expected no error on valid config, got %v", err) 523 } 524 } 525 526 /* 527 TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks 528 from an invalid chain to test if latestValidHash (LVH) works correctly. 529 530 We set up the following chain where P1 ... Pn and P1” are valid while 531 P1' is invalid. 532 We expect 533 (1) The LVH to point to the current inserted payload if it was valid. 534 (2) The LVH to point to the valid parent on an invalid payload (if the parent is available). 535 (3) If the parent is unavailable, the LVH should not be set. 536 537 CommonAncestor◄─▲── P1 ◄── P2 ◄─ P3 ◄─ ... ◄─ Pn 538 │ 539 └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn' 540 │ 541 └── P1'' 542 */ 543 func TestNewPayloadOnInvalidChain(t *testing.T) { 544 genesis, preMergeBlocks := generatePreMergeChain(10) 545 n, ethservice := startEthService(t, genesis, preMergeBlocks) 546 ethservice.Merger().ReachTTD() 547 defer n.Close() 548 549 var ( 550 api = NewConsensusAPI(ethservice) 551 parent = ethservice.BlockChain().CurrentBlock() 552 // This EVM code generates a log when the contract is created. 553 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 554 ) 555 for i := 0; i < 10; i++ { 556 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 557 nonce := statedb.GetNonce(testAddr) 558 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 559 ethservice.TxPool().AddLocal(tx) 560 561 params := beacon.PayloadAttributesV1{ 562 Timestamp: parent.Time() + 1, 563 Random: crypto.Keccak256Hash([]byte{byte(i)}), 564 SuggestedFeeRecipient: parent.Coinbase(), 565 } 566 567 fcState := beacon.ForkchoiceStateV1{ 568 HeadBlockHash: parent.Hash(), 569 SafeBlockHash: common.Hash{}, 570 FinalizedBlockHash: common.Hash{}, 571 } 572 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 573 if err != nil { 574 t.Fatalf("error preparing payload, err=%v", err) 575 } 576 if resp.PayloadStatus.Status != beacon.VALID { 577 t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) 578 } 579 payload, err := api.GetPayloadV1(*resp.PayloadID) 580 if err != nil { 581 t.Fatalf("can't get payload: %v", err) 582 } 583 // TODO(493456442, marius) this test can be flaky since we rely on a 100ms 584 // allowance for block generation internally. 585 if len(payload.Transactions) == 0 { 586 t.Fatalf("payload should not be empty") 587 } 588 execResp, err := api.NewPayloadV1(*payload) 589 if err != nil { 590 t.Fatalf("can't execute payload: %v", err) 591 } 592 if execResp.Status != beacon.VALID { 593 t.Fatalf("invalid status: %v", execResp.Status) 594 } 595 fcState = beacon.ForkchoiceStateV1{ 596 HeadBlockHash: payload.BlockHash, 597 SafeBlockHash: payload.ParentHash, 598 FinalizedBlockHash: payload.ParentHash, 599 } 600 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 601 t.Fatalf("Failed to insert block: %v", err) 602 } 603 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 604 t.Fatalf("Chain head should be updated") 605 } 606 parent = ethservice.BlockChain().CurrentBlock() 607 } 608 } 609 610 func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) { 611 block, err := api.eth.Miner().GetSealingBlockSync(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random, false) 612 if err != nil { 613 return nil, err 614 } 615 return beacon.BlockToExecutableData(block), nil 616 } 617 618 func TestEmptyBlocks(t *testing.T) { 619 genesis, preMergeBlocks := generatePreMergeChain(10) 620 n, ethservice := startEthService(t, genesis, preMergeBlocks) 621 ethservice.Merger().ReachTTD() 622 defer n.Close() 623 624 commonAncestor := ethservice.BlockChain().CurrentBlock() 625 api := NewConsensusAPI(ethservice) 626 627 // Setup 10 blocks on the canonical chain 628 setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {}) 629 630 // (1) check LatestValidHash by sending a normal payload (P1'') 631 payload := getNewPayload(t, api, commonAncestor) 632 633 status, err := api.NewPayloadV1(*payload) 634 if err != nil { 635 t.Fatal(err) 636 } 637 if status.Status != beacon.VALID { 638 t.Errorf("invalid status: expected VALID got: %v", status.Status) 639 } 640 if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) { 641 t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash) 642 } 643 644 // (2) Now send P1' which is invalid 645 payload = getNewPayload(t, api, commonAncestor) 646 payload.GasUsed += 1 647 payload = setBlockhash(payload) 648 // Now latestValidHash should be the common ancestor 649 status, err = api.NewPayloadV1(*payload) 650 if err != nil { 651 t.Fatal(err) 652 } 653 if status.Status != beacon.INVALID { 654 t.Errorf("invalid status: expected INVALID got: %v", status.Status) 655 } 656 expected := commonAncestor.Hash() 657 if !bytes.Equal(status.LatestValidHash[:], expected[:]) { 658 t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected) 659 } 660 661 // (3) Now send a payload with unknown parent 662 payload = getNewPayload(t, api, commonAncestor) 663 payload.ParentHash = common.Hash{1} 664 payload = setBlockhash(payload) 665 // Now latestValidHash should be the common ancestor 666 status, err = api.NewPayloadV1(*payload) 667 if err != nil { 668 t.Fatal(err) 669 } 670 if status.Status != beacon.ACCEPTED { 671 t.Errorf("invalid status: expected ACCEPTED got: %v", status.Status) 672 } 673 if status.LatestValidHash != nil { 674 t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash) 675 } 676 } 677 678 func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 { 679 params := beacon.PayloadAttributesV1{ 680 Timestamp: parent.Time() + 1, 681 Random: crypto.Keccak256Hash([]byte{byte(1)}), 682 SuggestedFeeRecipient: parent.Coinbase(), 683 } 684 685 payload, err := assembleBlock(api, parent.Hash(), ¶ms) 686 if err != nil { 687 t.Fatal(err) 688 } 689 return payload 690 } 691 692 // setBlockhash sets the blockhash of a modified ExecutableData. 693 // Can be used to make modified payloads look valid. 694 func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 { 695 txs, _ := decodeTransactions(data.Transactions) 696 number := big.NewInt(0) 697 number.SetUint64(data.Number) 698 header := &types.Header{ 699 ParentHash: data.ParentHash, 700 UncleHash: types.EmptyUncleHash, 701 Coinbase: data.FeeRecipient, 702 Root: data.StateRoot, 703 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 704 ReceiptHash: data.ReceiptsRoot, 705 Bloom: types.BytesToBloom(data.LogsBloom), 706 Difficulty: common.Big0, 707 Number: number, 708 GasLimit: data.GasLimit, 709 GasUsed: data.GasUsed, 710 Time: data.Timestamp, 711 BaseFee: data.BaseFeePerGas, 712 Extra: data.ExtraData, 713 MixDigest: data.Random, 714 } 715 block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) 716 data.BlockHash = block.Hash() 717 return data 718 } 719 720 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 721 var txs = make([]*types.Transaction, len(enc)) 722 for i, encTx := range enc { 723 var tx types.Transaction 724 if err := tx.UnmarshalBinary(encTx); err != nil { 725 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 726 } 727 txs[i] = &tx 728 } 729 return txs, nil 730 } 731 732 func TestTrickRemoteBlockCache(t *testing.T) { 733 // Setup two nodes 734 genesis, preMergeBlocks := generatePreMergeChain(10) 735 nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks) 736 nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks) 737 ethserviceA.Merger().ReachTTD() 738 ethserviceB.Merger().ReachTTD() 739 defer nodeA.Close() 740 defer nodeB.Close() 741 for nodeB.Server().NodeInfo().Ports.Listener == 0 { 742 time.Sleep(250 * time.Millisecond) 743 } 744 nodeA.Server().AddPeer(nodeB.Server().Self()) 745 nodeB.Server().AddPeer(nodeA.Server().Self()) 746 apiA := NewConsensusAPI(ethserviceA) 747 apiB := NewConsensusAPI(ethserviceB) 748 749 commonAncestor := ethserviceA.BlockChain().CurrentBlock() 750 751 // Setup 10 blocks on the canonical chain 752 setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {}) 753 commonAncestor = ethserviceA.BlockChain().CurrentBlock() 754 755 var invalidChain []*beacon.ExecutableDataV1 756 // create a valid payload (P1) 757 //payload1 := getNewPayload(t, apiA, commonAncestor) 758 //invalidChain = append(invalidChain, payload1) 759 760 // create an invalid payload2 (P2) 761 payload2 := getNewPayload(t, apiA, commonAncestor) 762 //payload2.ParentHash = payload1.BlockHash 763 payload2.GasUsed += 1 764 payload2 = setBlockhash(payload2) 765 invalidChain = append(invalidChain, payload2) 766 767 head := payload2 768 // create some valid payloads on top 769 for i := 0; i < 10; i++ { 770 payload := getNewPayload(t, apiA, commonAncestor) 771 payload.ParentHash = head.BlockHash 772 payload = setBlockhash(payload) 773 invalidChain = append(invalidChain, payload) 774 head = payload 775 } 776 777 // feed the payloads to node B 778 for _, payload := range invalidChain { 779 status, err := apiB.NewPayloadV1(*payload) 780 if err != nil { 781 panic(err) 782 } 783 if status.Status == beacon.INVALID { 784 panic("success") 785 } 786 // Now reorg to the head of the invalid chain 787 resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil) 788 if err != nil { 789 t.Fatal(err) 790 } 791 if resp.PayloadStatus.Status == beacon.VALID { 792 t.Errorf("invalid status: expected INVALID got: %v", resp.PayloadStatus.Status) 793 } 794 time.Sleep(100 * time.Millisecond) 795 } 796 }