github.com/ethereum/go-ethereum@v1.16.1/eth/tracers/internal/tracetest/supply_test.go (about) 1 // Copyright 2024 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 tracetest 18 19 import ( 20 "bufio" 21 "bytes" 22 "encoding/json" 23 "fmt" 24 "math/big" 25 "os" 26 "path" 27 "path/filepath" 28 "testing" 29 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/common/hexutil" 32 "github.com/ethereum/go-ethereum/consensus/beacon" 33 "github.com/ethereum/go-ethereum/consensus/ethash" 34 "github.com/ethereum/go-ethereum/core" 35 "github.com/ethereum/go-ethereum/core/rawdb" 36 "github.com/ethereum/go-ethereum/core/types" 37 "github.com/ethereum/go-ethereum/core/vm" 38 "github.com/ethereum/go-ethereum/crypto" 39 "github.com/ethereum/go-ethereum/eth/tracers" 40 "github.com/ethereum/go-ethereum/params" 41 42 // Force-load live packages, to trigger registration 43 _ "github.com/ethereum/go-ethereum/eth/tracers/live" 44 ) 45 46 type supplyInfoIssuance struct { 47 GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` 48 Reward *hexutil.Big `json:"reward,omitempty"` 49 Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` 50 } 51 52 type supplyInfoBurn struct { 53 EIP1559 *hexutil.Big `json:"1559,omitempty"` 54 Blob *hexutil.Big `json:"blob,omitempty"` 55 Misc *hexutil.Big `json:"misc,omitempty"` 56 } 57 58 type supplyInfo struct { 59 Issuance *supplyInfoIssuance `json:"issuance,omitempty"` 60 Burn *supplyInfoBurn `json:"burn,omitempty"` 61 62 // Block info 63 Number uint64 `json:"blockNumber"` 64 Hash common.Hash `json:"hash"` 65 ParentHash common.Hash `json:"parentHash"` 66 } 67 68 func emptyBlockGenerationFunc(b *core.BlockGen) {} 69 70 func TestSupplyOmittedFields(t *testing.T) { 71 var ( 72 config = *params.MergedTestChainConfig 73 gspec = &core.Genesis{ 74 Config: &config, 75 } 76 ) 77 78 out, _, err := testSupplyTracer(t, gspec, func(b *core.BlockGen) { 79 b.SetPoS() 80 }) 81 if err != nil { 82 t.Fatalf("failed to test supply tracer: %v", err) 83 } 84 85 expected := supplyInfo{ 86 Number: 0, 87 Hash: common.HexToHash("0x3055fc27d6b4a08eb07033a0d1ee755a4b2988086f28a6189eac1b507525eeb1"), 88 ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), 89 } 90 actual := out[expected.Number] 91 92 compareAsJSON(t, expected, actual) 93 } 94 95 func TestSupplyGenesisAlloc(t *testing.T) { 96 var ( 97 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 98 key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 99 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 100 addr2 = crypto.PubkeyToAddress(key2.PublicKey) 101 eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) 102 103 config = *params.AllEthashProtocolChanges 104 105 gspec = &core.Genesis{ 106 Config: &config, 107 Alloc: types.GenesisAlloc{ 108 addr1: {Balance: eth1}, 109 addr2: {Balance: eth1}, 110 }, 111 } 112 ) 113 114 expected := supplyInfo{ 115 Issuance: &supplyInfoIssuance{ 116 GenesisAlloc: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), 117 }, 118 Number: 0, 119 Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), 120 ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), 121 } 122 123 out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) 124 if err != nil { 125 t.Fatalf("failed to test supply tracer: %v", err) 126 } 127 128 actual := out[expected.Number] 129 130 compareAsJSON(t, expected, actual) 131 } 132 133 func TestSupplyRewards(t *testing.T) { 134 var ( 135 config = *params.AllEthashProtocolChanges 136 137 gspec = &core.Genesis{ 138 Config: &config, 139 } 140 ) 141 142 expected := supplyInfo{ 143 Issuance: &supplyInfoIssuance{ 144 Reward: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), 145 }, 146 Number: 1, 147 Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), 148 ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), 149 } 150 151 out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) 152 if err != nil { 153 t.Fatalf("failed to test supply tracer: %v", err) 154 } 155 156 actual := out[expected.Number] 157 158 compareAsJSON(t, expected, actual) 159 } 160 161 func TestSupplyEip1559Burn(t *testing.T) { 162 var ( 163 config = *params.AllEthashProtocolChanges 164 165 aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") 166 // A sender who makes transactions, has some eth1 167 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 168 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 169 gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) 170 eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) 171 172 gspec = &core.Genesis{ 173 Config: &config, 174 BaseFee: big.NewInt(params.InitialBaseFee), 175 Alloc: types.GenesisAlloc{ 176 addr1: {Balance: eth1}, 177 }, 178 } 179 ) 180 181 signer := types.LatestSigner(gspec.Config) 182 183 eip1559BlockGenerationFunc := func(b *core.BlockGen) { 184 txdata := &types.DynamicFeeTx{ 185 ChainID: gspec.Config.ChainID, 186 Nonce: 0, 187 To: &aa, 188 Gas: 21000, 189 GasFeeCap: gwei5, 190 GasTipCap: big.NewInt(2), 191 } 192 tx := types.NewTx(txdata) 193 tx, _ = types.SignTx(tx, signer, key1) 194 195 b.AddTx(tx) 196 } 197 198 out, chain, err := testSupplyTracer(t, gspec, eip1559BlockGenerationFunc) 199 if err != nil { 200 t.Fatalf("failed to test supply tracer: %v", err) 201 } 202 var ( 203 head = chain.CurrentBlock() 204 reward = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) 205 burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) 206 expected = supplyInfo{ 207 Issuance: &supplyInfoIssuance{ 208 Reward: (*hexutil.Big)(reward), 209 }, 210 Burn: &supplyInfoBurn{ 211 EIP1559: (*hexutil.Big)(burn), 212 }, 213 Number: 1, 214 Hash: head.Hash(), 215 ParentHash: head.ParentHash, 216 } 217 ) 218 219 actual := out[expected.Number] 220 compareAsJSON(t, expected, actual) 221 } 222 223 func TestSupplyWithdrawals(t *testing.T) { 224 var ( 225 config = *params.MergedTestChainConfig 226 gspec = &core.Genesis{ 227 Config: &config, 228 } 229 ) 230 231 withdrawalsBlockGenerationFunc := func(b *core.BlockGen) { 232 b.SetPoS() 233 234 b.AddWithdrawal(&types.Withdrawal{ 235 Validator: 42, 236 Address: common.Address{0xee}, 237 Amount: 1337, 238 }) 239 } 240 241 out, chain, err := testSupplyTracer(t, gspec, withdrawalsBlockGenerationFunc) 242 if err != nil { 243 t.Fatalf("failed to test supply tracer: %v", err) 244 } 245 246 var ( 247 head = chain.CurrentBlock() 248 expected = supplyInfo{ 249 Issuance: &supplyInfoIssuance{ 250 Withdrawals: (*hexutil.Big)(big.NewInt(1337000000000)), 251 }, 252 Number: 1, 253 Hash: head.Hash(), 254 ParentHash: head.ParentHash, 255 } 256 actual = out[expected.Number] 257 ) 258 259 compareAsJSON(t, expected, actual) 260 } 261 262 // Tests fund retrieval after contract's selfdestruct. 263 // Contract A calls contract B which selfdestructs, but B receives eth1 264 // after the selfdestruct opcode executes from Contract A. 265 // Because Contract B is removed only at the end of the transaction 266 // the ether sent in between is burnt before Cancun hard fork. 267 func TestSupplySelfdestruct(t *testing.T) { 268 var ( 269 config = *params.TestChainConfig 270 271 aa = common.HexToAddress("0x1111111111111111111111111111111111111111") 272 bb = common.HexToAddress("0x2222222222222222222222222222222222222222") 273 dad = common.HexToAddress("0x0000000000000000000000000000000000000dad") 274 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 275 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 276 gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) 277 eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) 278 279 gspec = &core.Genesis{ 280 Config: &config, 281 BaseFee: big.NewInt(params.InitialBaseFee), 282 Alloc: types.GenesisAlloc{ 283 addr1: {Balance: eth1}, 284 aa: { 285 Code: common.FromHex("0x61face60f01b6000527322222222222222222222222222222222222222226000806002600080855af160008103603457600080fd5b60008060008034865af1905060008103604c57600080fd5b5050"), 286 // Nonce: 0, 287 Balance: big.NewInt(0), 288 }, 289 bb: { 290 Code: common.FromHex("0x6000357fface000000000000000000000000000000000000000000000000000000000000808203602f57610dad80ff5b5050"), 291 Nonce: 0, 292 Balance: eth1, 293 }, 294 }, 295 } 296 ) 297 298 gspec.Config.TerminalTotalDifficulty = big.NewInt(0) 299 300 signer := types.LatestSigner(gspec.Config) 301 302 testBlockGenerationFunc := func(b *core.BlockGen) { 303 b.SetPoS() 304 305 txdata := &types.LegacyTx{ 306 Nonce: 0, 307 To: &aa, 308 Value: gwei5, 309 Gas: 150000, 310 GasPrice: gwei5, 311 Data: []byte{}, 312 } 313 314 tx := types.NewTx(txdata) 315 tx, _ = types.SignTx(tx, signer, key1) 316 317 b.AddTx(tx) 318 } 319 320 // 1. Test pre Cancun 321 preCancunOutput, preCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) 322 if err != nil { 323 t.Fatalf("Pre-cancun failed to test supply tracer: %v", err) 324 } 325 326 // Check balance at state: 327 // 1. 0x0000...000dad has 1 ether 328 // 2. A has 0 ether 329 // 3. B has 0 ether 330 statedb, _ := preCancunChain.State() 331 if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 { 332 t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", dad, got, exp) 333 } 334 if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 { 335 t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", aa, got, exp) 336 } 337 if got, exp := statedb.GetBalance(bb), big.NewInt(0); got.CmpBig(exp) != 0 { 338 t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", bb, got, exp) 339 } 340 341 head := preCancunChain.CurrentBlock() 342 // Check live trace output 343 expected := supplyInfo{ 344 Burn: &supplyInfoBurn{ 345 EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), 346 Misc: (*hexutil.Big)(big.NewInt(5000000000)), 347 }, 348 Number: 1, 349 Hash: head.Hash(), 350 ParentHash: head.ParentHash, 351 } 352 353 actual := preCancunOutput[expected.Number] 354 355 compareAsJSON(t, expected, actual) 356 357 // 2. Test post Cancun 358 cancunTime := uint64(0) 359 gspec.Config.ShanghaiTime = &cancunTime 360 gspec.Config.CancunTime = &cancunTime 361 gspec.Config.BlobScheduleConfig = params.DefaultBlobSchedule 362 363 postCancunOutput, postCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) 364 if err != nil { 365 t.Fatalf("Post-cancun failed to test supply tracer: %v", err) 366 } 367 368 // Check balance at state: 369 // 1. 0x0000...000dad has 1 ether 370 // 3. A has 0 ether 371 // 3. B has 5 gwei 372 statedb, _ = postCancunChain.State() 373 if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 { 374 t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", dad, got, exp) 375 } 376 if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 { 377 t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", aa, got, exp) 378 } 379 if got, exp := statedb.GetBalance(bb), gwei5; got.CmpBig(exp) != 0 { 380 t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", bb, got, exp) 381 } 382 383 // Check live trace output 384 head = postCancunChain.CurrentBlock() 385 expected = supplyInfo{ 386 Burn: &supplyInfoBurn{ 387 EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), 388 }, 389 Number: 1, 390 Hash: head.Hash(), 391 ParentHash: head.ParentHash, 392 } 393 394 actual = postCancunOutput[expected.Number] 395 396 compareAsJSON(t, expected, actual) 397 } 398 399 // Tests selfdestructing contract to send its balance to itself (burn). 400 // It tests both cases of selfdestructing succeeding and being reverted. 401 // - Contract A calls B and D. 402 // - Contract B selfdestructs and sends the eth1 to itself (Burn amount to be counted). 403 // - Contract C selfdestructs and sends the eth1 to itself. 404 // - Contract D calls C and reverts (Burn amount of C 405 // has to be reverted as well). 406 func TestSupplySelfdestructItselfAndRevert(t *testing.T) { 407 var ( 408 config = *params.TestChainConfig 409 410 aa = common.HexToAddress("0x1111111111111111111111111111111111111111") 411 bb = common.HexToAddress("0x2222222222222222222222222222222222222222") 412 cc = common.HexToAddress("0x3333333333333333333333333333333333333333") 413 dd = common.HexToAddress("0x4444444444444444444444444444444444444444") 414 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 415 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 416 gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) 417 eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) 418 eth2 = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) 419 eth5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.Ether)) 420 421 gspec = &core.Genesis{ 422 Config: &config, 423 // BaseFee: big.NewInt(params.InitialBaseFee), 424 Alloc: types.GenesisAlloc{ 425 addr1: {Balance: eth1}, 426 aa: { 427 // Contract code in YUL: 428 // 429 // object "ContractA" { 430 // code { 431 // let B := 0x2222222222222222222222222222222222222222 432 // let D := 0x4444444444444444444444444444444444444444 433 434 // // Call to Contract B 435 // let resB:= call(gas(), B, 0, 0x0, 0x0, 0, 0) 436 437 // // Call to Contract D 438 // let resD := call(gas(), D, 0, 0x0, 0x0, 0, 0) 439 // } 440 // } 441 Code: common.FromHex("0x73222222222222222222222222222222222222222273444444444444444444444444444444444444444460006000600060006000865af160006000600060006000865af150505050"), 442 Balance: common.Big0, 443 }, 444 bb: { 445 // Contract code in YUL: 446 // 447 // object "ContractB" { 448 // code { 449 // let self := address() 450 // selfdestruct(self) 451 // } 452 // } 453 Code: common.FromHex("0x3080ff50"), 454 Balance: eth5, 455 }, 456 cc: { 457 Code: common.FromHex("0x3080ff50"), 458 Balance: eth1, 459 }, 460 dd: { 461 // Contract code in YUL: 462 // 463 // object "ContractD" { 464 // code { 465 // let C := 0x3333333333333333333333333333333333333333 466 467 // // Call to Contract C 468 // let resC := call(gas(), C, 0, 0x0, 0x0, 0, 0) 469 470 // // Revert 471 // revert(0, 0) 472 // } 473 // } 474 Code: common.FromHex("0x73333333333333333333333333333333333333333360006000600060006000855af160006000fd5050"), 475 Balance: eth2, 476 }, 477 }, 478 } 479 ) 480 481 gspec.Config.TerminalTotalDifficulty = big.NewInt(0) 482 483 signer := types.LatestSigner(gspec.Config) 484 485 testBlockGenerationFunc := func(b *core.BlockGen) { 486 b.SetPoS() 487 488 txdata := &types.LegacyTx{ 489 Nonce: 0, 490 To: &aa, 491 Value: common.Big0, 492 Gas: 150000, 493 GasPrice: gwei5, 494 Data: []byte{}, 495 } 496 497 tx := types.NewTx(txdata) 498 tx, _ = types.SignTx(tx, signer, key1) 499 500 b.AddTx(tx) 501 } 502 503 output, chain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) 504 if err != nil { 505 t.Fatalf("failed to test supply tracer: %v", err) 506 } 507 508 // Check balance at state: 509 // 1. A has 0 ether 510 // 2. B has 0 ether, burned 511 // 3. C has 2 ether, selfdestructed but parent D reverted 512 // 4. D has 1 ether, reverted 513 statedb, _ := chain.State() 514 if got, exp := statedb.GetBalance(aa), common.Big0; got.CmpBig(exp) != 0 { 515 t.Fatalf("address \"%v\" balance, got %v exp %v\n", aa, got, exp) 516 } 517 if got, exp := statedb.GetBalance(bb), common.Big0; got.CmpBig(exp) != 0 { 518 t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) 519 } 520 if got, exp := statedb.GetBalance(cc), eth1; got.CmpBig(exp) != 0 { 521 t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) 522 } 523 if got, exp := statedb.GetBalance(dd), eth2; got.CmpBig(exp) != 0 { 524 t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) 525 } 526 527 // Check live trace output 528 block := chain.GetBlockByNumber(1) 529 530 expected := supplyInfo{ 531 Burn: &supplyInfoBurn{ 532 EIP1559: (*hexutil.Big)(new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed())))), 533 Misc: (*hexutil.Big)(eth5), // 5ETH burned from contract B 534 }, 535 Number: 1, 536 Hash: block.Hash(), 537 ParentHash: block.ParentHash(), 538 } 539 540 actual := output[expected.Number] 541 542 compareAsJSON(t, expected, actual) 543 } 544 545 func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) { 546 engine := beacon.New(ethash.NewFaker()) 547 548 traceOutputPath := filepath.ToSlash(t.TempDir()) 549 traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") 550 551 // Load supply tracer 552 tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) 553 if err != nil { 554 return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) 555 } 556 557 options := core.DefaultConfig().WithStateScheme(rawdb.PathScheme) 558 options.VmConfig = vm.Config{Tracer: tracer} 559 chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), genesis, engine, options) 560 if err != nil { 561 return nil, nil, fmt.Errorf("failed to create tester chain: %v", err) 562 } 563 defer chain.Stop() 564 565 _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, 1, func(i int, b *core.BlockGen) { 566 b.SetCoinbase(common.Address{1}) 567 gen(b) 568 }) 569 570 if n, err := chain.InsertChain(blocks); err != nil { 571 return nil, chain, fmt.Errorf("block %d: failed to insert into chain: %v", n, err) 572 } 573 574 // Check and compare the results 575 file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666) 576 if err != nil { 577 return nil, chain, fmt.Errorf("failed to open output file: %v", err) 578 } 579 defer file.Close() 580 581 var output []supplyInfo 582 scanner := bufio.NewScanner(file) 583 584 for scanner.Scan() { 585 blockBytes := scanner.Bytes() 586 587 var info supplyInfo 588 if err := json.Unmarshal(blockBytes, &info); err != nil { 589 return nil, chain, fmt.Errorf("failed to unmarshal result: %v", err) 590 } 591 592 output = append(output, info) 593 } 594 595 return output, chain, nil 596 } 597 598 func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { 599 t.Helper() 600 want, err := json.Marshal(expected) 601 if err != nil { 602 t.Fatalf("failed to marshal expected value to JSON: %v", err) 603 } 604 605 have, err := json.Marshal(actual) 606 if err != nil { 607 t.Fatalf("failed to marshal actual value to JSON: %v", err) 608 } 609 610 if !bytes.Equal(want, have) { 611 t.Fatalf("incorrect supply info:\nwant %s\nhave %s", string(want), string(have)) 612 } 613 }