github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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 t.Skip("bor due to burn contract") 81 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 execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams) 97 if err != nil { 98 t.Fatalf("error producing block, err=%v", err) 99 } 100 if len(execData.Transactions) != 1 { 101 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 102 } 103 } 104 105 func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { 106 t.Skip("bor due to burn contract") 107 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.insertTransactions(blocks[9].Transactions()) 116 blockParams := beacon.PayloadAttributesV1{ 117 Timestamp: blocks[8].Time() + 5, 118 } 119 execData, err := api.assembleBlock(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.INVALIDTERMINALBLOCK { 142 t.Errorf("fork choice updated before total terminal difficulty should be INVALID") 143 } 144 } 145 146 func TestEth2PrepareAndGetPayload(t *testing.T) { 147 // TODO (MariusVanDerWijden) TestEth2PrepareAndGetPayload is currently broken, fixed in upcoming merge-kiln-v2 pr 148 /* 149 genesis, blocks := generatePreMergeChain(10) 150 // We need to properly set the terminal total difficulty 151 genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty()) 152 n, ethservice := startEthService(t, genesis, blocks[:9]) 153 defer n.Close() 154 155 api := NewConsensusAPI(ethservice) 156 157 // Put the 10th block's tx in the pool and produce a new block 158 api.insertTransactions(blocks[9].Transactions()) 159 blockParams := beacon.PayloadAttributesV1{ 160 Timestamp: blocks[8].Time() + 5, 161 } 162 fcState := beacon.ForkchoiceStateV1{ 163 HeadBlockHash: blocks[8].Hash(), 164 SafeBlockHash: common.Hash{}, 165 FinalizedBlockHash: common.Hash{}, 166 } 167 _, err := api.ForkchoiceUpdatedV1(fcState, &blockParams) 168 if err != nil { 169 t.Fatalf("error preparing payload, err=%v", err) 170 } 171 payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams) 172 execData, err := api.GetPayloadV1(payloadID) 173 if err != nil { 174 t.Fatalf("error getting payload, err=%v", err) 175 } 176 if len(execData.Transactions) != blocks[9].Transactions().Len() { 177 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 178 } 179 // Test invalid payloadID 180 var invPayload beacon.PayloadID 181 copy(invPayload[:], payloadID[:]) 182 invPayload[0] = ^invPayload[0] 183 _, err = api.GetPayloadV1(invPayload) 184 if err == nil { 185 t.Fatal("expected error retrieving invalid payload") 186 } 187 */ 188 } 189 190 func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { 191 t.Helper() 192 193 if len(logsCh) != wantNew { 194 t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew) 195 } 196 if len(rmLogsCh) != wantRemoved { 197 t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved) 198 } 199 // Drain events. 200 for i := 0; i < len(logsCh); i++ { 201 <-logsCh 202 } 203 for i := 0; i < len(rmLogsCh); i++ { 204 <-rmLogsCh 205 } 206 } 207 208 func TestInvalidPayloadTimestamp(t *testing.T) { 209 genesis, preMergeBlocks := generatePreMergeChain(10) 210 n, ethservice := startEthService(t, genesis, preMergeBlocks) 211 ethservice.Merger().ReachTTD() 212 defer n.Close() 213 var ( 214 api = NewConsensusAPI(ethservice) 215 parent = ethservice.BlockChain().CurrentBlock() 216 ) 217 tests := []struct { 218 time uint64 219 shouldErr bool 220 }{ 221 {0, true}, 222 {parent.Time(), true}, 223 {parent.Time() - 1, true}, 224 225 // TODO (MariusVanDerWijden) following tests are currently broken, 226 // fixed in upcoming merge-kiln-v2 pr 227 //{parent.Time() + 1, false}, 228 //{uint64(time.Now().Unix()) + uint64(time.Minute), false}, 229 } 230 231 for i, test := range tests { 232 t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) { 233 params := beacon.PayloadAttributesV1{ 234 Timestamp: test.time, 235 Random: crypto.Keccak256Hash([]byte{byte(123)}), 236 SuggestedFeeRecipient: parent.Coinbase(), 237 } 238 fcState := beacon.ForkchoiceStateV1{ 239 HeadBlockHash: parent.Hash(), 240 SafeBlockHash: common.Hash{}, 241 FinalizedBlockHash: common.Hash{}, 242 } 243 _, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 244 if test.shouldErr && err == nil { 245 t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err) 246 } else if !test.shouldErr && err != nil { 247 t.Fatalf("error preparing payload with valid timestamp, err=%v", err) 248 } 249 }) 250 } 251 } 252 253 func TestEth2NewBlock(t *testing.T) { 254 t.Skip("ETH2 in Bor") 255 genesis, preMergeBlocks := generatePreMergeChain(10) 256 n, ethservice := startEthService(t, genesis, preMergeBlocks) 257 ethservice.Merger().ReachTTD() 258 defer n.Close() 259 260 var ( 261 api = NewConsensusAPI(ethservice) 262 parent = preMergeBlocks[len(preMergeBlocks)-1] 263 264 // This EVM code generates a log when the contract is created. 265 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 266 ) 267 // The event channels. 268 newLogCh := make(chan []*types.Log, 10) 269 rmLogsCh := make(chan core.RemovedLogsEvent, 10) 270 ethservice.BlockChain().SubscribeLogsEvent(newLogCh) 271 ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) 272 273 for i := 0; i < 10; i++ { 274 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 275 nonce := statedb.GetNonce(testAddr) 276 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 277 ethservice.TxPool().AddLocal(tx) 278 279 execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{ 280 Timestamp: parent.Time() + 5, 281 }) 282 if err != nil { 283 t.Fatalf("Failed to create the executable data %v", err) 284 } 285 block, err := beacon.ExecutableDataToBlock(*execData) 286 if err != nil { 287 t.Fatalf("Failed to convert executable data to block %v", err) 288 } 289 newResp, err := api.NewPayloadV1(*execData) 290 if err != nil || newResp.Status != "VALID" { 291 t.Fatalf("Failed to insert block: %v", err) 292 } 293 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { 294 t.Fatalf("Chain head shouldn't be updated") 295 } 296 checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) 297 fcState := beacon.ForkchoiceStateV1{ 298 HeadBlockHash: block.Hash(), 299 SafeBlockHash: block.Hash(), 300 FinalizedBlockHash: block.Hash(), 301 } 302 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 303 t.Fatalf("Failed to insert block: %v", err) 304 } 305 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 306 t.Fatalf("Chain head should be updated") 307 } 308 checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) 309 310 parent = block 311 } 312 313 // Introduce fork chain 314 var ( 315 head = ethservice.BlockChain().CurrentBlock().NumberU64() 316 ) 317 parent = preMergeBlocks[len(preMergeBlocks)-1] 318 for i := 0; i < 10; i++ { 319 execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{ 320 Timestamp: parent.Time() + 6, 321 }) 322 if err != nil { 323 t.Fatalf("Failed to create the executable data %v", err) 324 } 325 block, err := beacon.ExecutableDataToBlock(*execData) 326 if err != nil { 327 t.Fatalf("Failed to convert executable data to block %v", err) 328 } 329 newResp, err := api.NewPayloadV1(*execData) 330 if err != nil || newResp.Status != "VALID" { 331 t.Fatalf("Failed to insert block: %v", err) 332 } 333 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 334 t.Fatalf("Chain head shouldn't be updated") 335 } 336 337 fcState := beacon.ForkchoiceStateV1{ 338 HeadBlockHash: block.Hash(), 339 SafeBlockHash: block.Hash(), 340 FinalizedBlockHash: block.Hash(), 341 } 342 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 343 t.Fatalf("Failed to insert block: %v", err) 344 } 345 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 346 t.Fatalf("Chain head should be updated") 347 } 348 parent, head = block, block.NumberU64() 349 } 350 } 351 352 func TestEth2DeepReorg(t *testing.T) { 353 // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg 354 // before the totalTerminalDifficulty threshold 355 /* 356 genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) 357 n, ethservice := startEthService(t, genesis, preMergeBlocks) 358 defer n.Close() 359 360 var ( 361 api = NewConsensusAPI(ethservice, nil) 362 parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] 363 head = ethservice.BlockChain().CurrentBlock().NumberU64() 364 ) 365 if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { 366 t.Errorf("Block %d not pruned", parent.NumberU64()) 367 } 368 for i := 0; i < 10; i++ { 369 execData, err := api.assembleBlock(AssembleBlockParams{ 370 ParentHash: parent.Hash(), 371 Timestamp: parent.Time() + 5, 372 }) 373 if err != nil { 374 t.Fatalf("Failed to create the executable data %v", err) 375 } 376 block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) 377 if err != nil { 378 t.Fatalf("Failed to convert executable data to block %v", err) 379 } 380 newResp, err := api.ExecutePayload(*execData) 381 if err != nil || newResp.Status != "VALID" { 382 t.Fatalf("Failed to insert block: %v", err) 383 } 384 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 385 t.Fatalf("Chain head shouldn't be updated") 386 } 387 if err := api.setHead(block.Hash()); err != nil { 388 t.Fatalf("Failed to set head: %v", err) 389 } 390 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 391 t.Fatalf("Chain head should be updated") 392 } 393 parent, head = block, block.NumberU64() 394 } 395 */ 396 } 397 398 // startEthService creates a full node instance for testing. 399 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { 400 t.Helper() 401 402 n, err := node.New(&node.Config{}) 403 if err != nil { 404 t.Fatal("can't create node:", err) 405 } 406 407 ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} 408 ethservice, err := eth.New(n, ethcfg) 409 if err != nil { 410 t.Fatal("can't create eth service:", err) 411 } 412 if err := n.Start(); err != nil { 413 t.Fatal("can't start node:", err) 414 } 415 if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { 416 n.Close() 417 t.Fatal("can't import test blocks:", err) 418 } 419 ethservice.SetEtherbase(testAddr) 420 ethservice.SetSynced() 421 422 return n, ethservice 423 } 424 425 func TestFullAPI(t *testing.T) { 426 t.Skip("ETH2 in Bor") 427 428 genesis, preMergeBlocks := generatePreMergeChain(10) 429 n, ethservice := startEthService(t, genesis, preMergeBlocks) 430 ethservice.Merger().ReachTTD() 431 defer n.Close() 432 var ( 433 api = NewConsensusAPI(ethservice) 434 parent = ethservice.BlockChain().CurrentBlock() 435 // This EVM code generates a log when the contract is created. 436 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 437 ) 438 for i := 0; i < 10; i++ { 439 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 440 nonce := statedb.GetNonce(testAddr) 441 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 442 ethservice.TxPool().AddLocal(tx) 443 444 params := beacon.PayloadAttributesV1{ 445 Timestamp: parent.Time() + 1, 446 Random: crypto.Keccak256Hash([]byte{byte(i)}), 447 SuggestedFeeRecipient: parent.Coinbase(), 448 } 449 450 fcState := beacon.ForkchoiceStateV1{ 451 HeadBlockHash: parent.Hash(), 452 SafeBlockHash: common.Hash{}, 453 FinalizedBlockHash: common.Hash{}, 454 } 455 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 456 if err != nil { 457 t.Fatalf("error preparing payload, err=%v", err) 458 } 459 if resp.PayloadStatus.Status != beacon.VALID { 460 t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) 461 } 462 payload, err := api.GetPayloadV1(*resp.PayloadID) 463 if err != nil { 464 t.Fatalf("can't get payload: %v", err) 465 } 466 execResp, err := api.NewPayloadV1(*payload) 467 if err != nil { 468 t.Fatalf("can't execute payload: %v", err) 469 } 470 if execResp.Status != beacon.VALID { 471 t.Fatalf("invalid status: %v", execResp.Status) 472 } 473 fcState = beacon.ForkchoiceStateV1{ 474 HeadBlockHash: payload.BlockHash, 475 SafeBlockHash: payload.ParentHash, 476 FinalizedBlockHash: payload.ParentHash, 477 } 478 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 479 t.Fatalf("Failed to insert block: %v", err) 480 } 481 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 482 t.Fatalf("Chain head should be updated") 483 } 484 parent = ethservice.BlockChain().CurrentBlock() 485 } 486 } 487 488 func TestExchangeTransitionConfig(t *testing.T) { 489 genesis, preMergeBlocks := generatePreMergeChain(10) 490 n, ethservice := startEthService(t, genesis, preMergeBlocks) 491 ethservice.Merger().ReachTTD() 492 defer n.Close() 493 var ( 494 api = NewConsensusAPI(ethservice) 495 ) 496 // invalid ttd 497 config := beacon.TransitionConfigurationV1{ 498 TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)), 499 TerminalBlockHash: common.Hash{}, 500 TerminalBlockNumber: 0, 501 } 502 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 503 t.Fatal("expected error on invalid config, invalid ttd") 504 } 505 // invalid terminal block hash 506 config = beacon.TransitionConfigurationV1{ 507 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 508 TerminalBlockHash: common.Hash{1}, 509 TerminalBlockNumber: 0, 510 } 511 if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil { 512 t.Fatal("expected error on invalid config, invalid hash") 513 } 514 // valid config 515 config = beacon.TransitionConfigurationV1{ 516 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 517 TerminalBlockHash: common.Hash{}, 518 TerminalBlockNumber: 0, 519 } 520 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 521 t.Fatalf("expected no error on valid config, got %v", err) 522 } 523 // valid config 524 config = beacon.TransitionConfigurationV1{ 525 TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty), 526 TerminalBlockHash: preMergeBlocks[5].Hash(), 527 TerminalBlockNumber: 6, 528 } 529 if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil { 530 t.Fatalf("expected no error on valid config, got %v", err) 531 } 532 }