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