github.com/LampardNguyen234/go-ethereum@v1.10.16-0.20220117140830-b6a3b0260724/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 "math/big" 21 "testing" 22 "time" 23 24 "github.com/LampardNguyen234/go-ethereum/common" 25 "github.com/LampardNguyen234/go-ethereum/common/hexutil" 26 "github.com/LampardNguyen234/go-ethereum/consensus/ethash" 27 "github.com/LampardNguyen234/go-ethereum/core" 28 "github.com/LampardNguyen234/go-ethereum/core/rawdb" 29 "github.com/LampardNguyen234/go-ethereum/core/types" 30 "github.com/LampardNguyen234/go-ethereum/crypto" 31 "github.com/LampardNguyen234/go-ethereum/eth" 32 "github.com/LampardNguyen234/go-ethereum/eth/ethconfig" 33 "github.com/LampardNguyen234/go-ethereum/node" 34 "github.com/LampardNguyen234/go-ethereum/params" 35 ) 36 37 var ( 38 // testKey is a private key to use for funding a tester account. 39 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 40 41 // testAddr is the Ethereum address of the tester account. 42 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 43 44 testBalance = big.NewInt(2e18) 45 ) 46 47 func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { 48 db := rawdb.NewMemoryDatabase() 49 config := params.AllEthashProtocolChanges 50 genesis := &core.Genesis{ 51 Config: config, 52 Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, 53 ExtraData: []byte("test genesis"), 54 Timestamp: 9000, 55 BaseFee: big.NewInt(params.InitialBaseFee), 56 } 57 testNonce := uint64(0) 58 generate := func(i int, g *core.BlockGen) { 59 g.OffsetTime(5) 60 g.SetExtra([]byte("test")) 61 tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey) 62 g.AddTx(tx) 63 testNonce++ 64 } 65 gblock := genesis.ToBlock(db) 66 engine := ethash.NewFaker() 67 blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) 68 totalDifficulty := big.NewInt(0) 69 for _, b := range blocks { 70 totalDifficulty.Add(totalDifficulty, b.Difficulty()) 71 } 72 config.TerminalTotalDifficulty = totalDifficulty 73 return genesis, blocks 74 } 75 76 func TestEth2AssembleBlock(t *testing.T) { 77 genesis, blocks := generatePreMergeChain(10) 78 n, ethservice := startEthService(t, genesis, blocks) 79 defer n.Close() 80 81 api := NewConsensusAPI(ethservice, nil) 82 signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) 83 tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) 84 if err != nil { 85 t.Fatalf("error signing transaction, err=%v", err) 86 } 87 ethservice.TxPool().AddLocal(tx) 88 blockParams := PayloadAttributesV1{ 89 Timestamp: blocks[9].Time() + 5, 90 } 91 execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams) 92 if err != nil { 93 t.Fatalf("error producing block, err=%v", err) 94 } 95 if len(execData.Transactions) != 1 { 96 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 97 } 98 } 99 100 func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { 101 genesis, blocks := generatePreMergeChain(10) 102 n, ethservice := startEthService(t, genesis, blocks[:9]) 103 defer n.Close() 104 105 api := NewConsensusAPI(ethservice, nil) 106 107 // Put the 10th block's tx in the pool and produce a new block 108 api.insertTransactions(blocks[9].Transactions()) 109 blockParams := PayloadAttributesV1{ 110 Timestamp: blocks[8].Time() + 5, 111 } 112 execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams) 113 if err != nil { 114 t.Fatalf("error producing block, err=%v", err) 115 } 116 if len(execData.Transactions) != blocks[9].Transactions().Len() { 117 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 118 } 119 } 120 121 func TestSetHeadBeforeTotalDifficulty(t *testing.T) { 122 genesis, blocks := generatePreMergeChain(10) 123 n, ethservice := startEthService(t, genesis, blocks) 124 defer n.Close() 125 126 api := NewConsensusAPI(ethservice, nil) 127 fcState := ForkchoiceStateV1{ 128 HeadBlockHash: blocks[5].Hash(), 129 SafeBlockHash: common.Hash{}, 130 FinalizedBlockHash: common.Hash{}, 131 } 132 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err == nil { 133 t.Errorf("fork choice updated before total terminal difficulty should fail") 134 } 135 } 136 137 func TestEth2PrepareAndGetPayload(t *testing.T) { 138 genesis, blocks := generatePreMergeChain(10) 139 // We need to properly set the terminal total difficulty 140 genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty()) 141 n, ethservice := startEthService(t, genesis, blocks[:9]) 142 defer n.Close() 143 144 api := NewConsensusAPI(ethservice, nil) 145 146 // Put the 10th block's tx in the pool and produce a new block 147 api.insertTransactions(blocks[9].Transactions()) 148 blockParams := PayloadAttributesV1{ 149 Timestamp: blocks[8].Time() + 5, 150 } 151 fcState := ForkchoiceStateV1{ 152 HeadBlockHash: blocks[8].Hash(), 153 SafeBlockHash: common.Hash{}, 154 FinalizedBlockHash: common.Hash{}, 155 } 156 _, err := api.ForkchoiceUpdatedV1(fcState, &blockParams) 157 if err != nil { 158 t.Fatalf("error preparing payload, err=%v", err) 159 } 160 payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams) 161 execData, err := api.GetPayloadV1(hexutil.Bytes(payloadID)) 162 if err != nil { 163 t.Fatalf("error getting payload, err=%v", err) 164 } 165 if len(execData.Transactions) != blocks[9].Transactions().Len() { 166 t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) 167 } 168 } 169 170 func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { 171 t.Helper() 172 173 if len(logsCh) != wantNew { 174 t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew) 175 } 176 if len(rmLogsCh) != wantRemoved { 177 t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved) 178 } 179 // Drain events. 180 for i := 0; i < len(logsCh); i++ { 181 <-logsCh 182 } 183 for i := 0; i < len(rmLogsCh); i++ { 184 <-rmLogsCh 185 } 186 } 187 188 func TestEth2NewBlock(t *testing.T) { 189 genesis, preMergeBlocks := generatePreMergeChain(10) 190 n, ethservice := startEthService(t, genesis, preMergeBlocks) 191 ethservice.Merger().ReachTTD() 192 defer n.Close() 193 194 var ( 195 api = NewConsensusAPI(ethservice, nil) 196 parent = preMergeBlocks[len(preMergeBlocks)-1] 197 198 // This EVM code generates a log when the contract is created. 199 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 200 ) 201 // The event channels. 202 newLogCh := make(chan []*types.Log, 10) 203 rmLogsCh := make(chan core.RemovedLogsEvent, 10) 204 ethservice.BlockChain().SubscribeLogsEvent(newLogCh) 205 ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) 206 207 for i := 0; i < 10; i++ { 208 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 209 nonce := statedb.GetNonce(testAddr) 210 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 211 ethservice.TxPool().AddLocal(tx) 212 213 execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{ 214 Timestamp: parent.Time() + 5, 215 }) 216 if err != nil { 217 t.Fatalf("Failed to create the executable data %v", err) 218 } 219 block, err := ExecutableDataToBlock(*execData) 220 if err != nil { 221 t.Fatalf("Failed to convert executable data to block %v", err) 222 } 223 newResp, err := api.ExecutePayloadV1(*execData) 224 if err != nil || newResp.Status != "VALID" { 225 t.Fatalf("Failed to insert block: %v", err) 226 } 227 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { 228 t.Fatalf("Chain head shouldn't be updated") 229 } 230 checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) 231 fcState := ForkchoiceStateV1{ 232 HeadBlockHash: block.Hash(), 233 SafeBlockHash: block.Hash(), 234 FinalizedBlockHash: block.Hash(), 235 } 236 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 237 t.Fatalf("Failed to insert block: %v", err) 238 } 239 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 240 t.Fatalf("Chain head should be updated") 241 } 242 checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) 243 244 parent = block 245 } 246 247 // Introduce fork chain 248 var ( 249 head = ethservice.BlockChain().CurrentBlock().NumberU64() 250 ) 251 parent = preMergeBlocks[len(preMergeBlocks)-1] 252 for i := 0; i < 10; i++ { 253 execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{ 254 Timestamp: parent.Time() + 6, 255 }) 256 if err != nil { 257 t.Fatalf("Failed to create the executable data %v", err) 258 } 259 block, err := ExecutableDataToBlock(*execData) 260 if err != nil { 261 t.Fatalf("Failed to convert executable data to block %v", err) 262 } 263 newResp, err := api.ExecutePayloadV1(*execData) 264 if err != nil || newResp.Status != "VALID" { 265 t.Fatalf("Failed to insert block: %v", err) 266 } 267 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 268 t.Fatalf("Chain head shouldn't be updated") 269 } 270 271 fcState := ForkchoiceStateV1{ 272 HeadBlockHash: block.Hash(), 273 SafeBlockHash: block.Hash(), 274 FinalizedBlockHash: block.Hash(), 275 } 276 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 277 t.Fatalf("Failed to insert block: %v", err) 278 } 279 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 280 t.Fatalf("Chain head should be updated") 281 } 282 parent, head = block, block.NumberU64() 283 } 284 } 285 286 func TestEth2DeepReorg(t *testing.T) { 287 // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg 288 // before the totalTerminalDifficulty threshold 289 /* 290 genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) 291 n, ethservice := startEthService(t, genesis, preMergeBlocks) 292 defer n.Close() 293 294 var ( 295 api = NewConsensusAPI(ethservice, nil) 296 parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] 297 head = ethservice.BlockChain().CurrentBlock().NumberU64() 298 ) 299 if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { 300 t.Errorf("Block %d not pruned", parent.NumberU64()) 301 } 302 for i := 0; i < 10; i++ { 303 execData, err := api.assembleBlock(AssembleBlockParams{ 304 ParentHash: parent.Hash(), 305 Timestamp: parent.Time() + 5, 306 }) 307 if err != nil { 308 t.Fatalf("Failed to create the executable data %v", err) 309 } 310 block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) 311 if err != nil { 312 t.Fatalf("Failed to convert executable data to block %v", err) 313 } 314 newResp, err := api.ExecutePayload(*execData) 315 if err != nil || newResp.Status != "VALID" { 316 t.Fatalf("Failed to insert block: %v", err) 317 } 318 if ethservice.BlockChain().CurrentBlock().NumberU64() != head { 319 t.Fatalf("Chain head shouldn't be updated") 320 } 321 if err := api.setHead(block.Hash()); err != nil { 322 t.Fatalf("Failed to set head: %v", err) 323 } 324 if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { 325 t.Fatalf("Chain head should be updated") 326 } 327 parent, head = block, block.NumberU64() 328 } 329 */ 330 } 331 332 // startEthService creates a full node instance for testing. 333 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { 334 t.Helper() 335 336 n, err := node.New(&node.Config{}) 337 if err != nil { 338 t.Fatal("can't create node:", err) 339 } 340 341 ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} 342 ethservice, err := eth.New(n, ethcfg) 343 if err != nil { 344 t.Fatal("can't create eth service:", err) 345 } 346 if err := n.Start(); err != nil { 347 t.Fatal("can't start node:", err) 348 } 349 if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { 350 n.Close() 351 t.Fatal("can't import test blocks:", err) 352 } 353 ethservice.SetEtherbase(testAddr) 354 ethservice.SetSynced() 355 356 return n, ethservice 357 } 358 359 func TestFullAPI(t *testing.T) { 360 genesis, preMergeBlocks := generatePreMergeChain(10) 361 n, ethservice := startEthService(t, genesis, preMergeBlocks) 362 ethservice.Merger().ReachTTD() 363 defer n.Close() 364 var ( 365 api = NewConsensusAPI(ethservice, nil) 366 parent = ethservice.BlockChain().CurrentBlock() 367 // This EVM code generates a log when the contract is created. 368 logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") 369 ) 370 for i := 0; i < 10; i++ { 371 statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) 372 nonce := statedb.GetNonce(testAddr) 373 tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) 374 ethservice.TxPool().AddLocal(tx) 375 376 params := PayloadAttributesV1{ 377 Timestamp: parent.Time() + 1, 378 Random: crypto.Keccak256Hash([]byte{byte(i)}), 379 SuggestedFeeRecipient: parent.Coinbase(), 380 } 381 fcState := ForkchoiceStateV1{ 382 HeadBlockHash: parent.Hash(), 383 SafeBlockHash: common.Hash{}, 384 FinalizedBlockHash: common.Hash{}, 385 } 386 resp, err := api.ForkchoiceUpdatedV1(fcState, ¶ms) 387 if err != nil { 388 t.Fatalf("error preparing payload, err=%v", err) 389 } 390 if resp.Status != SUCCESS.Status { 391 t.Fatalf("error preparing payload, invalid status: %v", resp.Status) 392 } 393 payloadID := computePayloadId(parent.Hash(), ¶ms) 394 payload, err := api.GetPayloadV1(hexutil.Bytes(payloadID)) 395 if err != nil { 396 t.Fatalf("can't get payload: %v", err) 397 } 398 execResp, err := api.ExecutePayloadV1(*payload) 399 if err != nil { 400 t.Fatalf("can't execute payload: %v", err) 401 } 402 if execResp.Status != VALID.Status { 403 t.Fatalf("invalid status: %v", execResp.Status) 404 } 405 fcState = ForkchoiceStateV1{ 406 HeadBlockHash: payload.BlockHash, 407 SafeBlockHash: payload.ParentHash, 408 FinalizedBlockHash: payload.ParentHash, 409 } 410 if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { 411 t.Fatalf("Failed to insert block: %v", err) 412 } 413 if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { 414 t.Fatalf("Chain head should be updated") 415 } 416 parent = ethservice.BlockChain().CurrentBlock() 417 418 } 419 }