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