github.com/bcnmy/go-ethereum@v1.10.27/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/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/common/hexutil" 28 "github.com/ethereum/go-ethereum/consensus/ethash" 29 "github.com/ethereum/go-ethereum/core" 30 "github.com/ethereum/go-ethereum/core/beacon" 31 "github.com/ethereum/go-ethereum/core/rawdb" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/crypto" 34 "github.com/ethereum/go-ethereum/eth" 35 "github.com/ethereum/go-ethereum/eth/downloader" 36 "github.com/ethereum/go-ethereum/eth/ethconfig" 37 "github.com/ethereum/go-ethereum/node" 38 "github.com/ethereum/go-ethereum/p2p" 39 "github.com/ethereum/go-ethereum/params" 40 "github.com/ethereum/go-ethereum/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.MustCommit(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 defer n.Close() 209 var ( 210 api = NewConsensusAPI(ethservice) 211 parent = ethservice.BlockChain().CurrentBlock() 212 ) 213 tests := []struct { 214 time uint64 215 shouldErr bool 216 }{ 217 {0, true}, 218 {parent.Time(), true}, 219 {parent.Time() - 1, true}, 220 221 // TODO (MariusVanDerWijden) following tests are currently broken, 222 // fixed in upcoming merge-kiln-v2 pr 223 //{parent.Time() + 1, false}, 224 //{uint64(time.Now().Unix()) + uint64(time.Minute), false}, 225 } 226 227 for i, test := range tests { 228 t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) { 229 params := beacon.PayloadAttributesV1{ 230 Timestamp: test.time, 231 Random: crypto.Keccak256Hash([]byte{byte(123)}), 232 SuggestedFeeRecipient: parent.Coinbase(), 233 } 234 fcState := beacon.ForkchoiceStateV1{ 235 HeadBlockHash: parent.Hash(), 236 SafeBlockHash: common.Hash{}, 237 FinalizedBlockHash: common.Hash{}, 238 } 239 _, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 240 if test.shouldErr && err == nil { 241 t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err) 242 } else if !test.shouldErr && err != nil { 243 t.Fatalf("error preparing payload with valid timestamp, err=%v", err) 244 } 245 }) 246 } 247 } 248 249 func TestEth2NewBlock(t *testing.T) { 250 genesis, preMergeBlocks := generatePreMergeChain(10) 251 n, ethservice := startEthService(t, genesis, preMergeBlocks) 252 defer n.Close() 253 254 var ( 255 api = NewConsensusAPI(ethservice) 256 parent = preMergeBlocks[len(preMergeBlocks)-1] 257 258 // This EVM code generates a log when the contract is created. 259 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 260 ) 261 // The event channels. 262 newLogCh := make(chan []*types.Log, 10) 263 rmLogsCh := make(chan core.RemovedLogsEvent, 10) 264 ethservice.BlockChain().SubscribeLogsEvent(newLogCh) 265 ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) 266 267 for i := 0; i < 10; i++ { 268 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 269 nonce := statedb.GetNonce(testAddr) 270 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 271 ethservice.TxPool().AddLocal(tx) 272 273 execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{ 274 Timestamp: parent.Time() + 5, 275 }) 276 if err != nil { 277 t.Fatalf("Failed to create the executable data %v", err) 278 } 279 block, err := beacon.ExecutableDataToBlock(*execData) 280 if err != nil { 281 t.Fatalf("Failed to convert executable data to block %v", err) 282 } 283 newResp, err := api.NewPayloadV1(*execData) 284 if err != nil || newResp.Status != "VALID" { 285 t.Fatalf("Failed to insert block: %v", err) 286 } 287 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { 288 t.Fatalf("Chain head shouldn't be updated") 289 } 290 checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) 291 fcState := beacon.ForkchoiceStateV1{ 292 HeadBlockHash: block.Hash(), 293 SafeBlockHash: block.Hash(), 294 FinalizedBlockHash: block.Hash(), 295 } 296 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 297 t.Fatalf("Failed to insert block: %v", err) 298 } 299 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 300 t.Fatalf("Chain head should be updated") 301 } 302 checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) 303 304 parent = block 305 } 306 307 // Introduce fork chain 308 var ( 309 head = ethservice.BlockChain().CurrentBlock().NumberU64() 310 ) 311 parent = preMergeBlocks[len(preMergeBlocks)-1] 312 for i := 0; i < 10; i++ { 313 execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{ 314 Timestamp: parent.Time() + 6, 315 }) 316 if err != nil { 317 t.Fatalf("Failed to create the executable data %v", err) 318 } 319 block, err := beacon.ExecutableDataToBlock(*execData) 320 if err != nil { 321 t.Fatalf("Failed to convert executable data to block %v", err) 322 } 323 newResp, err := api.NewPayloadV1(*execData) 324 if err != nil || newResp.Status != "VALID" { 325 t.Fatalf("Failed to insert block: %v", err) 326 } 327 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 328 t.Fatalf("Chain head shouldn't be updated") 329 } 330 331 fcState := beacon.ForkchoiceStateV1{ 332 HeadBlockHash: block.Hash(), 333 SafeBlockHash: block.Hash(), 334 FinalizedBlockHash: block.Hash(), 335 } 336 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 337 t.Fatalf("Failed to insert block: %v", err) 338 } 339 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 340 t.Fatalf("Chain head should be updated") 341 } 342 parent, head = block, block.NumberU64() 343 } 344 } 345 346 func TestEth2DeepReorg(t *testing.T) { 347 // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg 348 // before the totalTerminalDifficulty threshold 349 /* 350 genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) 351 n, ethservice := startEthService(t, genesis, preMergeBlocks) 352 defer n.Close() 353 354 var ( 355 api = NewConsensusAPI(ethservice, nil) 356 parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] 357 head = ethservice.BlockChain().CurrentBlock().NumberU64() 358 ) 359 if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { 360 t.Errorf("Block %d not pruned", parent.NumberU64()) 361 } 362 for i := 0; i < 10; i++ { 363 execData, err := api.assembleBlock(AssembleBlockParams{ 364 ParentHash: parent.Hash(), 365 Timestamp: parent.Time() + 5, 366 }) 367 if err != nil { 368 t.Fatalf("Failed to create the executable data %v", err) 369 } 370 block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) 371 if err != nil { 372 t.Fatalf("Failed to convert executable data to block %v", err) 373 } 374 newResp, err := api.ExecutePayload(*execData) 375 if err != nil || newResp.Status != "VALID" { 376 t.Fatalf("Failed to insert block: %v", err) 377 } 378 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 379 t.Fatalf("Chain head shouldn't be updated") 380 } 381 if err := api.setHead(block.Hash()); err != nil { 382 t.Fatalf("Failed to set head: %v", err) 383 } 384 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 385 t.Fatalf("Chain head should be updated") 386 } 387 parent, head = block, block.NumberU64() 388 } 389 */ 390 } 391 392 // startEthService creates a full node instance for testing. 393 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { 394 t.Helper() 395 396 n, err := node.New(&node.Config{ 397 P2P: p2p.Config{ 398 ListenAddr: "0.0.0.0:0", 399 NoDiscovery: true, 400 MaxPeers: 25, 401 }}) 402 if err != nil { 403 t.Fatal("can't create node:", err) 404 } 405 406 ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} 407 ethservice, err := eth.New(n, ethcfg) 408 if err != nil { 409 t.Fatal("can't create eth service:", err) 410 } 411 if err := n.Start(); err != nil { 412 t.Fatal("can't start node:", err) 413 } 414 if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { 415 n.Close() 416 t.Fatal("can't import test blocks:", err) 417 } 418 time.Sleep(500 * time.Millisecond) // give txpool enough time to consume head event 419 420 ethservice.SetEtherbase(testAddr) 421 ethservice.SetSynced() 422 return n, ethservice 423 } 424 425 func TestFullAPI(t *testing.T) { 426 genesis, preMergeBlocks := generatePreMergeChain(10) 427 n, ethservice := startEthService(t, genesis, preMergeBlocks) 428 defer n.Close() 429 var ( 430 parent = ethservice.BlockChain().CurrentBlock() 431 // This EVM code generates a log when the contract is created. 432 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 433 ) 434 435 callback := func(parent *types.Block) { 436 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 437 nonce := statedb.GetNonce(testAddr) 438 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 439 ethservice.TxPool().AddLocal(tx) 440 } 441 442 setupBlocks(t, ethservice, 10, parent, callback) 443 } 444 445 func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) { 446 api := NewConsensusAPI(ethservice) 447 for i := 0; i < n; i++ { 448 callback(parent) 449 450 payload := getNewPayload(t, api, parent) 451 452 execResp, err := api.NewPayloadV1(*payload) 453 if err != nil { 454 t.Fatalf("can't execute payload: %v", err) 455 } 456 if execResp.Status != beacon.VALID { 457 t.Fatalf("invalid status: %v", execResp.Status) 458 } 459 fcState := beacon.ForkchoiceStateV1{ 460 HeadBlockHash: payload.BlockHash, 461 SafeBlockHash: payload.ParentHash, 462 FinalizedBlockHash: payload.ParentHash, 463 } 464 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 465 t.Fatalf("Failed to insert block: %v", err) 466 } 467 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 468 t.Fatal("Chain head should be updated") 469 } 470 if ethservice.BlockChain().CurrentFinalizedBlock().NumberU64() != payload.Number-1 { 471 t.Fatal("Finalized block should be updated") 472 } 473 parent = ethservice.BlockChain().CurrentBlock() 474 } 475 } 476 477 func TestExchangeTransitionConfig(t *testing.T) { 478 genesis, preMergeBlocks := generatePreMergeChain(10) 479 n, ethservice := startEthService(t, genesis, preMergeBlocks) 480 defer n.Close() 481 var ( 482 api = NewConsensusAPI(ethservice) 483 ) 484 // invalid ttd 485 config := beacon.TransitionConfigurationV1{ 486 TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)), 487 TerminalBlockHash: common.Hash{}, 488 TerminalBlockNumber: 0, 489 } 490 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 491 t.Fatal("expected error on invalid config, invalid ttd") 492 } 493 // invalid terminal block hash 494 config = beacon.TransitionConfigurationV1{ 495 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 496 TerminalBlockHash: common.Hash{1}, 497 TerminalBlockNumber: 0, 498 } 499 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 500 t.Fatal("expected error on invalid config, invalid hash") 501 } 502 // valid config 503 config = beacon.TransitionConfigurationV1{ 504 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 505 TerminalBlockHash: common.Hash{}, 506 TerminalBlockNumber: 0, 507 } 508 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 509 t.Fatalf("expected no error on valid config, got %v", err) 510 } 511 // valid config 512 config = beacon.TransitionConfigurationV1{ 513 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 514 TerminalBlockHash: preMergeBlocks[5].Hash(), 515 TerminalBlockNumber: 6, 516 } 517 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 518 t.Fatalf("expected no error on valid config, got %v", err) 519 } 520 } 521 522 /* 523 TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks 524 from an invalid chain to test if latestValidHash (LVH) works correctly. 525 526 We set up the following chain where P1 ... Pn and P1'' are valid while 527 P1' is invalid. 528 We expect 529 (1) The LVH to point to the current inserted payload if it was valid. 530 (2) The LVH to point to the valid parent on an invalid payload (if the parent is available). 531 (3) If the parent is unavailable, the LVH should not be set. 532 533 CommonAncestor◄─▲── P1 ◄── P2 ◄─ P3 ◄─ ... ◄─ Pn 534 │ 535 └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn' 536 │ 537 └── P1'' 538 */ 539 func TestNewPayloadOnInvalidChain(t *testing.T) { 540 genesis, preMergeBlocks := generatePreMergeChain(10) 541 n, ethservice := startEthService(t, genesis, preMergeBlocks) 542 defer n.Close() 543 544 var ( 545 api = NewConsensusAPI(ethservice) 546 parent = ethservice.BlockChain().CurrentBlock() 547 // This EVM code generates a log when the contract is created. 548 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 549 ) 550 for i := 0; i < 10; i++ { 551 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 552 nonce := statedb.GetNonce(testAddr) 553 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 554 ethservice.TxPool().AddLocal(tx) 555 556 params := beacon.PayloadAttributesV1{ 557 Timestamp: parent.Time() + 1, 558 Random: crypto.Keccak256Hash([]byte{byte(i)}), 559 SuggestedFeeRecipient: parent.Coinbase(), 560 } 561 562 fcState := beacon.ForkchoiceStateV1{ 563 HeadBlockHash: parent.Hash(), 564 SafeBlockHash: common.Hash{}, 565 FinalizedBlockHash: common.Hash{}, 566 } 567 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 568 if err != nil { 569 t.Fatalf("error preparing payload, err=%v", err) 570 } 571 if resp.PayloadStatus.Status != beacon.VALID { 572 t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) 573 } 574 payload, err := api.GetPayloadV1(*resp.PayloadID) 575 if err != nil { 576 t.Fatalf("can't get payload: %v", err) 577 } 578 // TODO(493456442, marius) this test can be flaky since we rely on a 100ms 579 // allowance for block generation internally. 580 if len(payload.Transactions) == 0 { 581 t.Fatalf("payload should not be empty") 582 } 583 execResp, err := api.NewPayloadV1(*payload) 584 if err != nil { 585 t.Fatalf("can't execute payload: %v", err) 586 } 587 if execResp.Status != beacon.VALID { 588 t.Fatalf("invalid status: %v", execResp.Status) 589 } 590 fcState = beacon.ForkchoiceStateV1{ 591 HeadBlockHash: payload.BlockHash, 592 SafeBlockHash: payload.ParentHash, 593 FinalizedBlockHash: payload.ParentHash, 594 } 595 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 596 t.Fatalf("Failed to insert block: %v", err) 597 } 598 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 599 t.Fatalf("Chain head should be updated") 600 } 601 parent = ethservice.BlockChain().CurrentBlock() 602 } 603 } 604 605 func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) { 606 block, err := api.eth.Miner().GetSealingBlockSync(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random, false) 607 if err != nil { 608 return nil, err 609 } 610 return beacon.BlockToExecutableData(block), nil 611 } 612 613 func TestEmptyBlocks(t *testing.T) { 614 genesis, preMergeBlocks := generatePreMergeChain(10) 615 n, ethservice := startEthService(t, genesis, preMergeBlocks) 616 defer n.Close() 617 618 commonAncestor := ethservice.BlockChain().CurrentBlock() 619 api := NewConsensusAPI(ethservice) 620 621 // Setup 10 blocks on the canonical chain 622 setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {}) 623 624 // (1) check LatestValidHash by sending a normal payload (P1'') 625 payload := getNewPayload(t, api, commonAncestor) 626 627 status, err := api.NewPayloadV1(*payload) 628 if err != nil { 629 t.Fatal(err) 630 } 631 if status.Status != beacon.VALID { 632 t.Errorf("invalid status: expected VALID got: %v", status.Status) 633 } 634 if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) { 635 t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash) 636 } 637 638 // (2) Now send P1' which is invalid 639 payload = getNewPayload(t, api, commonAncestor) 640 payload.GasUsed += 1 641 payload = setBlockhash(payload) 642 // Now latestValidHash should be the common ancestor 643 status, err = api.NewPayloadV1(*payload) 644 if err != nil { 645 t.Fatal(err) 646 } 647 if status.Status != beacon.INVALID { 648 t.Errorf("invalid status: expected INVALID got: %v", status.Status) 649 } 650 // Expect 0x0 on INVALID block on top of PoW block 651 expected := common.Hash{} 652 if !bytes.Equal(status.LatestValidHash[:], expected[:]) { 653 t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected) 654 } 655 656 // (3) Now send a payload with unknown parent 657 payload = getNewPayload(t, api, commonAncestor) 658 payload.ParentHash = common.Hash{1} 659 payload = setBlockhash(payload) 660 // Now latestValidHash should be the common ancestor 661 status, err = api.NewPayloadV1(*payload) 662 if err != nil { 663 t.Fatal(err) 664 } 665 if status.Status != beacon.SYNCING { 666 t.Errorf("invalid status: expected SYNCING got: %v", status.Status) 667 } 668 if status.LatestValidHash != nil { 669 t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash) 670 } 671 } 672 673 func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 { 674 params := beacon.PayloadAttributesV1{ 675 Timestamp: parent.Time() + 1, 676 Random: crypto.Keccak256Hash([]byte{byte(1)}), 677 SuggestedFeeRecipient: parent.Coinbase(), 678 } 679 680 payload, err := assembleBlock(api, parent.Hash(), ¶ms) 681 if err != nil { 682 t.Fatal(err) 683 } 684 return payload 685 } 686 687 // setBlockhash sets the blockhash of a modified ExecutableData. 688 // Can be used to make modified payloads look valid. 689 func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 { 690 txs, _ := decodeTransactions(data.Transactions) 691 number := big.NewInt(0) 692 number.SetUint64(data.Number) 693 header := &types.Header{ 694 ParentHash: data.ParentHash, 695 UncleHash: types.EmptyUncleHash, 696 Coinbase: data.FeeRecipient, 697 Root: data.StateRoot, 698 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 699 ReceiptHash: data.ReceiptsRoot, 700 Bloom: types.BytesToBloom(data.LogsBloom), 701 Difficulty: common.Big0, 702 Number: number, 703 GasLimit: data.GasLimit, 704 GasUsed: data.GasUsed, 705 Time: data.Timestamp, 706 BaseFee: data.BaseFeePerGas, 707 Extra: data.ExtraData, 708 MixDigest: data.Random, 709 } 710 block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) 711 data.BlockHash = block.Hash() 712 return data 713 } 714 715 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 716 var txs = make([]*types.Transaction, len(enc)) 717 for i, encTx := range enc { 718 var tx types.Transaction 719 if err := tx.UnmarshalBinary(encTx); err != nil { 720 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 721 } 722 txs[i] = &tx 723 } 724 return txs, nil 725 } 726 727 func TestTrickRemoteBlockCache(t *testing.T) { 728 // Setup two nodes 729 genesis, preMergeBlocks := generatePreMergeChain(10) 730 nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks) 731 nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks) 732 defer nodeA.Close() 733 defer nodeB.Close() 734 for nodeB.Server().NodeInfo().Ports.Listener == 0 { 735 time.Sleep(250 * time.Millisecond) 736 } 737 nodeA.Server().AddPeer(nodeB.Server().Self()) 738 nodeB.Server().AddPeer(nodeA.Server().Self()) 739 apiA := NewConsensusAPI(ethserviceA) 740 apiB := NewConsensusAPI(ethserviceB) 741 742 commonAncestor := ethserviceA.BlockChain().CurrentBlock() 743 744 // Setup 10 blocks on the canonical chain 745 setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {}) 746 commonAncestor = ethserviceA.BlockChain().CurrentBlock() 747 748 var invalidChain []*beacon.ExecutableDataV1 749 // create a valid payload (P1) 750 //payload1 := getNewPayload(t, apiA, commonAncestor) 751 //invalidChain = append(invalidChain, payload1) 752 753 // create an invalid payload2 (P2) 754 payload2 := getNewPayload(t, apiA, commonAncestor) 755 //payload2.ParentHash = payload1.BlockHash 756 payload2.GasUsed += 1 757 payload2 = setBlockhash(payload2) 758 invalidChain = append(invalidChain, payload2) 759 760 head := payload2 761 // create some valid payloads on top 762 for i := 0; i < 10; i++ { 763 payload := getNewPayload(t, apiA, commonAncestor) 764 payload.ParentHash = head.BlockHash 765 payload = setBlockhash(payload) 766 invalidChain = append(invalidChain, payload) 767 head = payload 768 } 769 770 // feed the payloads to node B 771 for _, payload := range invalidChain { 772 status, err := apiB.NewPayloadV1(*payload) 773 if err != nil { 774 panic(err) 775 } 776 if status.Status == beacon.VALID { 777 t.Error("invalid status: VALID on an invalid chain") 778 } 779 // Now reorg to the head of the invalid chain 780 resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil) 781 if err != nil { 782 t.Fatal(err) 783 } 784 if resp.PayloadStatus.Status == beacon.VALID { 785 t.Error("invalid status: VALID on an invalid chain") 786 } 787 time.Sleep(100 * time.Millisecond) 788 } 789 } 790 791 func TestInvalidBloom(t *testing.T) { 792 genesis, preMergeBlocks := generatePreMergeChain(10) 793 n, ethservice := startEthService(t, genesis, preMergeBlocks) 794 ethservice.Merger().ReachTTD() 795 defer n.Close() 796 797 commonAncestor := ethservice.BlockChain().CurrentBlock() 798 api := NewConsensusAPI(ethservice) 799 800 // Setup 10 blocks on the canonical chain 801 setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {}) 802 803 // (1) check LatestValidHash by sending a normal payload (P1'') 804 payload := getNewPayload(t, api, commonAncestor) 805 payload.LogsBloom = append(payload.LogsBloom, byte(1)) 806 status, err := api.NewPayloadV1(*payload) 807 if err != nil { 808 t.Fatal(err) 809 } 810 if status.Status != beacon.INVALIDBLOCKHASH { 811 t.Errorf("invalid status: expected VALID got: %v", status.Status) 812 } 813 } 814 815 func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) { 816 genesis, preMergeBlocks := generatePreMergeChain(100) 817 fmt.Println(genesis.Config.TerminalTotalDifficulty) 818 genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty()) 819 820 fmt.Println(genesis.Config.TerminalTotalDifficulty) 821 n, ethservice := startEthService(t, genesis, preMergeBlocks) 822 defer n.Close() 823 824 var ( 825 api = NewConsensusAPI(ethservice) 826 parent = preMergeBlocks[len(preMergeBlocks)-1] 827 ) 828 829 // Test parent already post TTD in FCU 830 fcState := beacon.ForkchoiceStateV1{ 831 HeadBlockHash: parent.Hash(), 832 SafeBlockHash: common.Hash{}, 833 FinalizedBlockHash: common.Hash{}, 834 } 835 resp, err := api.ForkchoiceUpdatedV1(fcState, nil) 836 if err != nil { 837 t.Fatalf("error sending forkchoice, err=%v", err) 838 } 839 if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK { 840 t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status) 841 } 842 843 // Test parent already post TTD in NewPayload 844 params := beacon.PayloadAttributesV1{ 845 Timestamp: parent.Time() + 1, 846 Random: crypto.Keccak256Hash([]byte{byte(1)}), 847 SuggestedFeeRecipient: parent.Coinbase(), 848 } 849 empty, err := api.eth.Miner().GetSealingBlockSync(parent.Hash(), params.Timestamp, params.SuggestedFeeRecipient, params.Random, true) 850 if err != nil { 851 t.Fatalf("error preparing payload, err=%v", err) 852 } 853 data := *beacon.BlockToExecutableData(empty) 854 resp2, err := api.NewPayloadV1(data) 855 if err != nil { 856 t.Fatalf("error sending NewPayload, err=%v", err) 857 } 858 if resp2 != beacon.INVALID_TERMINAL_BLOCK { 859 t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status) 860 } 861 }