github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/eth/catalyst/api_test.go (about) 1 // Copyright 2020 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 "fmt" 21 "math/big" 22 "testing" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/hexutil" 27 "github.com/ethereum/go-ethereum/consensus/ethash" 28 "github.com/ethereum/go-ethereum/core" 29 "github.com/ethereum/go-ethereum/core/beacon" 30 "github.com/ethereum/go-ethereum/core/rawdb" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/eth" 34 "github.com/ethereum/go-ethereum/eth/ethconfig" 35 "github.com/ethereum/go-ethereum/node" 36 "github.com/ethereum/go-ethereum/params" 37 ) 38 39 var ( 40 // testKey is a private key to use for funding a tester account. 41 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 42 43 // testAddr is the Ethereum address of the tester account. 44 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 45 46 testBalance = big.NewInt(2e18) 47 ) 48 49 func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { 50 db := rawdb.NewMemoryDatabase() 51 config := params.AllEthashProtocolChanges 52 genesis := &core.Genesis{ 53 Config: config, 54 Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, 55 ExtraData: []byte("test genesis"), 56 Timestamp: 9000, 57 BaseFee: big.NewInt(params.InitialBaseFee), 58 Difficulty: big.NewInt(0), 59 } 60 testNonce := uint64(0) 61 generate := func(i int, g *core.BlockGen) { 62 g.OffsetTime(5) 63 g.SetExtra([]byte("test")) 64 tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey) 65 g.AddTx(tx) 66 testNonce++ 67 } 68 gblock := genesis.ToBlock(db) 69 engine := ethash.NewFaker() 70 blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) 71 totalDifficulty := big.NewInt(0) 72 for _, b := range blocks { 73 totalDifficulty.Add(totalDifficulty, b.Difficulty()) 74 } 75 config.TerminalTotalDifficulty = totalDifficulty 76 return genesis, blocks 77 } 78 79 func TestEth2AssembleBlock(t *testing.T) { 80 genesis, blocks := generatePreMergeChain(10) 81 n, ethservice := startEthService(t, genesis, blocks) 82 defer n.Close() 83 84 api := NewConsensusAPI(ethservice) 85 signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) 86 tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) 87 if err != nil { 88 t.Fatalf("error signing transaction, err=%v", err) 89 } 90 ethservice.TxPool().AddLocal(tx) 91 blockParams := beacon.PayloadAttributesV1{ 92 Timestamp: blocks[9].Time() + 5, 93 } 94 execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams) 95 if err != nil { 96 t.Fatalf("error producing block, err=%v", err) 97 } 98 if len(execData.Transactions) != 1 { 99 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 100 } 101 } 102 103 func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { 104 genesis, blocks := generatePreMergeChain(10) 105 n, ethservice := startEthService(t, genesis, blocks[:9]) 106 defer n.Close() 107 108 api := NewConsensusAPI(ethservice) 109 110 // Put the 10th block's tx in the pool and produce a new block 111 api.insertTransactions(blocks[9].Transactions()) 112 blockParams := beacon.PayloadAttributesV1{ 113 Timestamp: blocks[8].Time() + 5, 114 } 115 execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams) 116 if err != nil { 117 t.Fatalf("error producing block, err=%v", err) 118 } 119 if len(execData.Transactions) != blocks[9].Transactions().Len() { 120 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 121 } 122 } 123 124 func TestSetHeadBeforeTotalDifficulty(t *testing.T) { 125 genesis, blocks := generatePreMergeChain(10) 126 n, ethservice := startEthService(t, genesis, blocks) 127 defer n.Close() 128 129 api := NewConsensusAPI(ethservice) 130 fcState := beacon.ForkchoiceStateV1{ 131 HeadBlockHash: blocks[5].Hash(), 132 SafeBlockHash: common.Hash{}, 133 FinalizedBlockHash: common.Hash{}, 134 } 135 if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 136 t.Errorf("fork choice updated should not error: %v", err) 137 } else if resp.PayloadStatus.Status != beacon.INVALIDTERMINALBLOCK { 138 t.Errorf("fork choice updated before total terminal difficulty should be INVALID") 139 } 140 } 141 142 func TestEth2PrepareAndGetPayload(t *testing.T) { 143 // TODO (MariusVanDerWijden) TestEth2PrepareAndGetPayload is currently broken, fixed in upcoming merge-kiln-v2 pr 144 /* 145 genesis, blocks := generatePreMergeChain(10) 146 // We need to properly set the terminal total difficulty 147 genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty()) 148 n, ethservice := startEthService(t, genesis, blocks[:9]) 149 defer n.Close() 150 151 api := NewConsensusAPI(ethservice) 152 153 // Put the 10th block's tx in the pool and produce a new block 154 api.insertTransactions(blocks[9].Transactions()) 155 blockParams := beacon.PayloadAttributesV1{ 156 Timestamp: blocks[8].Time() + 5, 157 } 158 fcState := beacon.ForkchoiceStateV1{ 159 HeadBlockHash: blocks[8].Hash(), 160 SafeBlockHash: common.Hash{}, 161 FinalizedBlockHash: common.Hash{}, 162 } 163 _, err := api.ForkchoiceUpdatedV1(fcState, &blockParams) 164 if err != nil { 165 t.Fatalf("error preparing payload, err=%v", err) 166 } 167 payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams) 168 execData, err := api.GetPayloadV1(payloadID) 169 if err != nil { 170 t.Fatalf("error getting payload, err=%v", err) 171 } 172 if len(execData.Transactions) != blocks[9].Transactions().Len() { 173 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 174 } 175 // Test invalid payloadID 176 var invPayload beacon.PayloadID 177 copy(invPayload[:], payloadID[:]) 178 invPayload[0] = ^invPayload[0] 179 _, err = api.GetPayloadV1(invPayload) 180 if err == nil { 181 t.Fatal("expected error retrieving invalid payload") 182 } 183 */ 184 } 185 186 func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { 187 t.Helper() 188 189 if len(logsCh) != wantNew { 190 t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew) 191 } 192 if len(rmLogsCh) != wantRemoved { 193 t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved) 194 } 195 // Drain events. 196 for i := 0; i < len(logsCh); i++ { 197 <-logsCh 198 } 199 for i := 0; i < len(rmLogsCh); i++ { 200 <-rmLogsCh 201 } 202 } 203 204 func TestInvalidPayloadTimestamp(t *testing.T) { 205 genesis, preMergeBlocks := generatePreMergeChain(10) 206 n, ethservice := startEthService(t, genesis, preMergeBlocks) 207 ethservice.Merger().ReachTTD() 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 ethservice.Merger().ReachTTD() 253 defer n.Close() 254 255 var ( 256 api = NewConsensusAPI(ethservice) 257 parent = preMergeBlocks[len(preMergeBlocks)-1] 258 259 // This EVM code generates a log when the contract is created. 260 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 261 ) 262 // The event channels. 263 newLogCh := make(chan []*types.Log, 10) 264 rmLogsCh := make(chan core.RemovedLogsEvent, 10) 265 ethservice.BlockChain().SubscribeLogsEvent(newLogCh) 266 ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) 267 268 for i := 0; i < 10; i++ { 269 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 270 nonce := statedb.GetNonce(testAddr) 271 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 272 ethservice.TxPool().AddLocal(tx) 273 274 execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{ 275 Timestamp: parent.Time() + 5, 276 }) 277 if err != nil { 278 t.Fatalf("Failed to create the executable data %v", err) 279 } 280 block, err := beacon.ExecutableDataToBlock(*execData) 281 if err != nil { 282 t.Fatalf("Failed to convert executable data to block %v", err) 283 } 284 newResp, err := api.NewPayloadV1(*execData) 285 if err != nil || newResp.Status != "VALID" { 286 t.Fatalf("Failed to insert block: %v", err) 287 } 288 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { 289 t.Fatalf("Chain head shouldn't be updated") 290 } 291 checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) 292 fcState := beacon.ForkchoiceStateV1{ 293 HeadBlockHash: block.Hash(), 294 SafeBlockHash: block.Hash(), 295 FinalizedBlockHash: block.Hash(), 296 } 297 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 298 t.Fatalf("Failed to insert block: %v", err) 299 } 300 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 301 t.Fatalf("Chain head should be updated") 302 } 303 checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) 304 305 parent = block 306 } 307 308 // Introduce fork chain 309 var ( 310 head = ethservice.BlockChain().CurrentBlock().NumberU64() 311 ) 312 parent = preMergeBlocks[len(preMergeBlocks)-1] 313 for i := 0; i < 10; i++ { 314 execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{ 315 Timestamp: parent.Time() + 6, 316 }) 317 if err != nil { 318 t.Fatalf("Failed to create the executable data %v", err) 319 } 320 block, err := beacon.ExecutableDataToBlock(*execData) 321 if err != nil { 322 t.Fatalf("Failed to convert executable data to block %v", err) 323 } 324 newResp, err := api.NewPayloadV1(*execData) 325 if err != nil || newResp.Status != "VALID" { 326 t.Fatalf("Failed to insert block: %v", err) 327 } 328 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 329 t.Fatalf("Chain head shouldn't be updated") 330 } 331 332 fcState := beacon.ForkchoiceStateV1{ 333 HeadBlockHash: block.Hash(), 334 SafeBlockHash: block.Hash(), 335 FinalizedBlockHash: block.Hash(), 336 } 337 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 338 t.Fatalf("Failed to insert block: %v", err) 339 } 340 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 341 t.Fatalf("Chain head should be updated") 342 } 343 parent, head = block, block.NumberU64() 344 } 345 } 346 347 func TestEth2DeepReorg(t *testing.T) { 348 // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg 349 // before the totalTerminalDifficulty threshold 350 /* 351 genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) 352 n, ethservice := startEthService(t, genesis, preMergeBlocks) 353 defer n.Close() 354 355 var ( 356 api = NewConsensusAPI(ethservice, nil) 357 parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] 358 head = ethservice.BlockChain().CurrentBlock().NumberU64() 359 ) 360 if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { 361 t.Errorf("Block %d not pruned", parent.NumberU64()) 362 } 363 for i := 0; i < 10; i++ { 364 execData, err := api.assembleBlock(AssembleBlockParams{ 365 ParentHash: parent.Hash(), 366 Timestamp: parent.Time() + 5, 367 }) 368 if err != nil { 369 t.Fatalf("Failed to create the executable data %v", err) 370 } 371 block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) 372 if err != nil { 373 t.Fatalf("Failed to convert executable data to block %v", err) 374 } 375 newResp, err := api.ExecutePayload(*execData) 376 if err != nil || newResp.Status != "VALID" { 377 t.Fatalf("Failed to insert block: %v", err) 378 } 379 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 380 t.Fatalf("Chain head shouldn't be updated") 381 } 382 if err := api.setHead(block.Hash()); err != nil { 383 t.Fatalf("Failed to set head: %v", err) 384 } 385 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 386 t.Fatalf("Chain head should be updated") 387 } 388 parent, head = block, block.NumberU64() 389 } 390 */ 391 } 392 393 // startEthService creates a full node instance for testing. 394 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { 395 t.Helper() 396 397 n, err := node.New(&node.Config{}) 398 if err != nil { 399 t.Fatal("can't create node:", err) 400 } 401 402 ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} 403 ethservice, err := eth.New(n, ethcfg) 404 if err != nil { 405 t.Fatal("can't create eth service:", err) 406 } 407 if err := n.Start(); err != nil { 408 t.Fatal("can't start node:", err) 409 } 410 if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { 411 n.Close() 412 t.Fatal("can't import test blocks:", err) 413 } 414 ethservice.SetEtherbase(testAddr) 415 ethservice.SetSynced() 416 417 return n, ethservice 418 } 419 420 func TestFullAPI(t *testing.T) { 421 genesis, preMergeBlocks := generatePreMergeChain(10) 422 n, ethservice := startEthService(t, genesis, preMergeBlocks) 423 ethservice.Merger().ReachTTD() 424 defer n.Close() 425 var ( 426 api = NewConsensusAPI(ethservice) 427 parent = ethservice.BlockChain().CurrentBlock() 428 // This EVM code generates a log when the contract is created. 429 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 430 ) 431 for i := 0; i < 10; i++ { 432 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 433 nonce := statedb.GetNonce(testAddr) 434 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 435 ethservice.TxPool().AddLocal(tx) 436 437 params := beacon.PayloadAttributesV1{ 438 Timestamp: parent.Time() + 1, 439 Random: crypto.Keccak256Hash([]byte{byte(i)}), 440 SuggestedFeeRecipient: parent.Coinbase(), 441 } 442 443 fcState := beacon.ForkchoiceStateV1{ 444 HeadBlockHash: parent.Hash(), 445 SafeBlockHash: common.Hash{}, 446 FinalizedBlockHash: common.Hash{}, 447 } 448 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 449 if err != nil { 450 t.Fatalf("error preparing payload, err=%v", err) 451 } 452 if resp.PayloadStatus.Status != beacon.VALID { 453 t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) 454 } 455 payload, err := api.GetPayloadV1(*resp.PayloadID) 456 if err != nil { 457 t.Fatalf("can't get payload: %v", err) 458 } 459 execResp, err := api.NewPayloadV1(*payload) 460 if err != nil { 461 t.Fatalf("can't execute payload: %v", err) 462 } 463 if execResp.Status != beacon.VALID { 464 t.Fatalf("invalid status: %v", execResp.Status) 465 } 466 fcState = beacon.ForkchoiceStateV1{ 467 HeadBlockHash: payload.BlockHash, 468 SafeBlockHash: payload.ParentHash, 469 FinalizedBlockHash: payload.ParentHash, 470 } 471 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 472 t.Fatalf("Failed to insert block: %v", err) 473 } 474 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 475 t.Fatalf("Chain head should be updated") 476 } 477 parent = ethservice.BlockChain().CurrentBlock() 478 } 479 } 480 481 func TestExchangeTransitionConfig(t *testing.T) { 482 genesis, preMergeBlocks := generatePreMergeChain(10) 483 n, ethservice := startEthService(t, genesis, preMergeBlocks) 484 ethservice.Merger().ReachTTD() 485 defer n.Close() 486 var ( 487 api = NewConsensusAPI(ethservice) 488 ) 489 // invalid ttd 490 config := beacon.TransitionConfigurationV1{ 491 TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)), 492 TerminalBlockHash: common.Hash{}, 493 TerminalBlockNumber: 0, 494 } 495 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 496 t.Fatal("expected error on invalid config, invalid ttd") 497 } 498 // invalid terminal block hash 499 config = beacon.TransitionConfigurationV1{ 500 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 501 TerminalBlockHash: common.Hash{1}, 502 TerminalBlockNumber: 0, 503 } 504 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 505 t.Fatal("expected error on invalid config, invalid hash") 506 } 507 // valid config 508 config = beacon.TransitionConfigurationV1{ 509 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 510 TerminalBlockHash: common.Hash{}, 511 TerminalBlockNumber: 0, 512 } 513 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 514 t.Fatalf("expected no error on valid config, got %v", err) 515 } 516 // valid config 517 config = beacon.TransitionConfigurationV1{ 518 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 519 TerminalBlockHash: preMergeBlocks[5].Hash(), 520 TerminalBlockNumber: 6, 521 } 522 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 523 t.Fatalf("expected no error on valid config, got %v", err) 524 } 525 }