github.com/MikyChow/arbitrum-go-ethereum@v0.0.0-20230306102812-078da49636de/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/MikyChow/arbitrum-go-ethereum/common" 27 "github.com/MikyChow/arbitrum-go-ethereum/common/hexutil" 28 "github.com/MikyChow/arbitrum-go-ethereum/consensus/ethash" 29 "github.com/MikyChow/arbitrum-go-ethereum/core" 30 "github.com/MikyChow/arbitrum-go-ethereum/core/beacon" 31 "github.com/MikyChow/arbitrum-go-ethereum/core/rawdb" 32 "github.com/MikyChow/arbitrum-go-ethereum/core/types" 33 "github.com/MikyChow/arbitrum-go-ethereum/crypto" 34 "github.com/MikyChow/arbitrum-go-ethereum/eth" 35 "github.com/MikyChow/arbitrum-go-ethereum/eth/downloader" 36 "github.com/MikyChow/arbitrum-go-ethereum/eth/ethconfig" 37 "github.com/MikyChow/arbitrum-go-ethereum/node" 38 "github.com/MikyChow/arbitrum-go-ethereum/p2p" 39 "github.com/MikyChow/arbitrum-go-ethereum/params" 40 "github.com/MikyChow/arbitrum-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 │ 536 └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn' 537 │ 538 └── P1'' 539 */ 540 func TestNewPayloadOnInvalidChain(t *testing.T) { 541 genesis, preMergeBlocks := generatePreMergeChain(10) 542 n, ethservice := startEthService(t, genesis, preMergeBlocks) 543 defer n.Close() 544 545 var ( 546 api = NewConsensusAPI(ethservice) 547 parent = ethservice.BlockChain().CurrentBlock() 548 // This EVM code generates a log when the contract is created. 549 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 550 ) 551 for i := 0; i < 10; i++ { 552 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 553 nonce := statedb.GetNonce(testAddr) 554 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 555 ethservice.TxPool().AddLocal(tx) 556 557 params := beacon.PayloadAttributesV1{ 558 Timestamp: parent.Time() + 1, 559 Random: crypto.Keccak256Hash([]byte{byte(i)}), 560 SuggestedFeeRecipient: parent.Coinbase(), 561 } 562 563 fcState := beacon.ForkchoiceStateV1{ 564 HeadBlockHash: parent.Hash(), 565 SafeBlockHash: common.Hash{}, 566 FinalizedBlockHash: common.Hash{}, 567 } 568 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 569 if err != nil { 570 t.Fatalf("error preparing payload, err=%v", err) 571 } 572 if resp.PayloadStatus.Status != beacon.VALID { 573 t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) 574 } 575 payload, err := api.GetPayloadV1(*resp.PayloadID) 576 if err != nil { 577 t.Fatalf("can't get payload: %v", err) 578 } 579 // TODO(493456442, marius) this test can be flaky since we rely on a 100ms 580 // allowance for block generation internally. 581 if len(payload.Transactions) == 0 { 582 t.Fatalf("payload should not be empty") 583 } 584 execResp, err := api.NewPayloadV1(*payload) 585 if err != nil { 586 t.Fatalf("can't execute payload: %v", err) 587 } 588 if execResp.Status != beacon.VALID { 589 t.Fatalf("invalid status: %v", execResp.Status) 590 } 591 fcState = beacon.ForkchoiceStateV1{ 592 HeadBlockHash: payload.BlockHash, 593 SafeBlockHash: payload.ParentHash, 594 FinalizedBlockHash: payload.ParentHash, 595 } 596 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 597 t.Fatalf("Failed to insert block: %v", err) 598 } 599 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 600 t.Fatalf("Chain head should be updated") 601 } 602 parent = ethservice.BlockChain().CurrentBlock() 603 } 604 } 605 606 func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) { 607 block, err := api.eth.Miner().GetSealingBlockSync(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random, false) 608 if err != nil { 609 return nil, err 610 } 611 return beacon.BlockToExecutableData(block), nil 612 } 613 614 func TestEmptyBlocks(t *testing.T) { 615 genesis, preMergeBlocks := generatePreMergeChain(10) 616 n, ethservice := startEthService(t, genesis, preMergeBlocks) 617 defer n.Close() 618 619 commonAncestor := ethservice.BlockChain().CurrentBlock() 620 api := NewConsensusAPI(ethservice) 621 622 // Setup 10 blocks on the canonical chain 623 setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {}) 624 625 // (1) check LatestValidHash by sending a normal payload (P1'') 626 payload := getNewPayload(t, api, commonAncestor) 627 628 status, err := api.NewPayloadV1(*payload) 629 if err != nil { 630 t.Fatal(err) 631 } 632 if status.Status != beacon.VALID { 633 t.Errorf("invalid status: expected VALID got: %v", status.Status) 634 } 635 if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) { 636 t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash) 637 } 638 639 // (2) Now send P1' which is invalid 640 payload = getNewPayload(t, api, commonAncestor) 641 payload.GasUsed += 1 642 payload = setBlockhash(payload) 643 // Now latestValidHash should be the common ancestor 644 status, err = api.NewPayloadV1(*payload) 645 if err != nil { 646 t.Fatal(err) 647 } 648 if status.Status != beacon.INVALID { 649 t.Errorf("invalid status: expected INVALID got: %v", status.Status) 650 } 651 // Expect 0x0 on INVALID block on top of PoW block 652 expected := common.Hash{} 653 if !bytes.Equal(status.LatestValidHash[:], expected[:]) { 654 t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected) 655 } 656 657 // (3) Now send a payload with unknown parent 658 payload = getNewPayload(t, api, commonAncestor) 659 payload.ParentHash = common.Hash{1} 660 payload = setBlockhash(payload) 661 // Now latestValidHash should be the common ancestor 662 status, err = api.NewPayloadV1(*payload) 663 if err != nil { 664 t.Fatal(err) 665 } 666 if status.Status != beacon.SYNCING { 667 t.Errorf("invalid status: expected SYNCING got: %v", status.Status) 668 } 669 if status.LatestValidHash != nil { 670 t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash) 671 } 672 } 673 674 func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 { 675 params := beacon.PayloadAttributesV1{ 676 Timestamp: parent.Time() + 1, 677 Random: crypto.Keccak256Hash([]byte{byte(1)}), 678 SuggestedFeeRecipient: parent.Coinbase(), 679 } 680 681 payload, err := assembleBlock(api, parent.Hash(), ¶ms) 682 if err != nil { 683 t.Fatal(err) 684 } 685 return payload 686 } 687 688 // setBlockhash sets the blockhash of a modified ExecutableData. 689 // Can be used to make modified payloads look valid. 690 func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 { 691 txs, _ := decodeTransactions(data.Transactions) 692 number := big.NewInt(0) 693 number.SetUint64(data.Number) 694 header := &types.Header{ 695 ParentHash: data.ParentHash, 696 UncleHash: types.EmptyUncleHash, 697 Coinbase: data.FeeRecipient, 698 Root: data.StateRoot, 699 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 700 ReceiptHash: data.ReceiptsRoot, 701 Bloom: types.BytesToBloom(data.LogsBloom), 702 Difficulty: common.Big0, 703 Number: number, 704 GasLimit: data.GasLimit, 705 GasUsed: data.GasUsed, 706 Time: data.Timestamp, 707 BaseFee: data.BaseFeePerGas, 708 Extra: data.ExtraData, 709 MixDigest: data.Random, 710 } 711 block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) 712 data.BlockHash = block.Hash() 713 return data 714 } 715 716 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 717 var txs = make([]*types.Transaction, len(enc)) 718 for i, encTx := range enc { 719 var tx types.Transaction 720 if err := tx.UnmarshalBinary(encTx); err != nil { 721 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 722 } 723 txs[i] = &tx 724 } 725 return txs, nil 726 } 727 728 func TestTrickRemoteBlockCache(t *testing.T) { 729 // Setup two nodes 730 genesis, preMergeBlocks := generatePreMergeChain(10) 731 nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks) 732 nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks) 733 defer nodeA.Close() 734 defer nodeB.Close() 735 for nodeB.Server().NodeInfo().Ports.Listener == 0 { 736 time.Sleep(250 * time.Millisecond) 737 } 738 nodeA.Server().AddPeer(nodeB.Server().Self()) 739 nodeB.Server().AddPeer(nodeA.Server().Self()) 740 apiA := NewConsensusAPI(ethserviceA) 741 apiB := NewConsensusAPI(ethserviceB) 742 743 commonAncestor := ethserviceA.BlockChain().CurrentBlock() 744 745 // Setup 10 blocks on the canonical chain 746 setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {}) 747 commonAncestor = ethserviceA.BlockChain().CurrentBlock() 748 749 var invalidChain []*beacon.ExecutableDataV1 750 // create a valid payload (P1) 751 //payload1 := getNewPayload(t, apiA, commonAncestor) 752 //invalidChain = append(invalidChain, payload1) 753 754 // create an invalid payload2 (P2) 755 payload2 := getNewPayload(t, apiA, commonAncestor) 756 //payload2.ParentHash = payload1.BlockHash 757 payload2.GasUsed += 1 758 payload2 = setBlockhash(payload2) 759 invalidChain = append(invalidChain, payload2) 760 761 head := payload2 762 // create some valid payloads on top 763 for i := 0; i < 10; i++ { 764 payload := getNewPayload(t, apiA, commonAncestor) 765 payload.ParentHash = head.BlockHash 766 payload = setBlockhash(payload) 767 invalidChain = append(invalidChain, payload) 768 head = payload 769 } 770 771 // feed the payloads to node B 772 for _, payload := range invalidChain { 773 status, err := apiB.NewPayloadV1(*payload) 774 if err != nil { 775 panic(err) 776 } 777 if status.Status == beacon.VALID { 778 t.Error("invalid status: VALID on an invalid chain") 779 } 780 // Now reorg to the head of the invalid chain 781 resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil) 782 if err != nil { 783 t.Fatal(err) 784 } 785 if resp.PayloadStatus.Status == beacon.VALID { 786 t.Error("invalid status: VALID on an invalid chain") 787 } 788 time.Sleep(100 * time.Millisecond) 789 } 790 } 791 792 func TestInvalidBloom(t *testing.T) { 793 genesis, preMergeBlocks := generatePreMergeChain(10) 794 n, ethservice := startEthService(t, genesis, preMergeBlocks) 795 ethservice.Merger().ReachTTD() 796 defer n.Close() 797 798 commonAncestor := ethservice.BlockChain().CurrentBlock() 799 api := NewConsensusAPI(ethservice) 800 801 // Setup 10 blocks on the canonical chain 802 setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {}) 803 804 // (1) check LatestValidHash by sending a normal payload (P1'') 805 payload := getNewPayload(t, api, commonAncestor) 806 payload.LogsBloom = append(payload.LogsBloom, byte(1)) 807 status, err := api.NewPayloadV1(*payload) 808 if err != nil { 809 t.Fatal(err) 810 } 811 if status.Status != beacon.INVALIDBLOCKHASH { 812 t.Errorf("invalid status: expected VALID got: %v", status.Status) 813 } 814 } 815 816 func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) { 817 genesis, preMergeBlocks := generatePreMergeChain(100) 818 fmt.Println(genesis.Config.TerminalTotalDifficulty) 819 genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty()) 820 821 fmt.Println(genesis.Config.TerminalTotalDifficulty) 822 n, ethservice := startEthService(t, genesis, preMergeBlocks) 823 defer n.Close() 824 825 var ( 826 api = NewConsensusAPI(ethservice) 827 parent = preMergeBlocks[len(preMergeBlocks)-1] 828 ) 829 830 // Test parent already post TTD in FCU 831 fcState := beacon.ForkchoiceStateV1{ 832 HeadBlockHash: parent.Hash(), 833 SafeBlockHash: common.Hash{}, 834 FinalizedBlockHash: common.Hash{}, 835 } 836 resp, err := api.ForkchoiceUpdatedV1(fcState, nil) 837 if err != nil { 838 t.Fatalf("error sending forkchoice, err=%v", err) 839 } 840 if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK { 841 t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status) 842 } 843 844 // Test parent already post TTD in NewPayload 845 params := beacon.PayloadAttributesV1{ 846 Timestamp: parent.Time() + 1, 847 Random: crypto.Keccak256Hash([]byte{byte(1)}), 848 SuggestedFeeRecipient: parent.Coinbase(), 849 } 850 empty, err := api.eth.Miner().GetSealingBlockSync(parent.Hash(), params.Timestamp, params.SuggestedFeeRecipient, params.Random, true) 851 if err != nil { 852 t.Fatalf("error preparing payload, err=%v", err) 853 } 854 data := *beacon.BlockToExecutableData(empty) 855 resp2, err := api.NewPayloadV1(data) 856 if err != nil { 857 t.Fatalf("error sending NewPayload, err=%v", err) 858 } 859 if resp2 != beacon.INVALID_TERMINAL_BLOCK { 860 t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status) 861 } 862 }