From c686e305a34474d04f97c81ae06898e867902005 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Tue, 26 Mar 2024 11:30:24 +0200 Subject: [PATCH 01/28] add BalanceChangeReason String() for better logging --- core/tracing/hooks.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 48cb4d20275c..406d34f9da40 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -214,6 +214,44 @@ const ( BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 ) +// String returns a string representation of the reason. +func (r BalanceChangeReason) String() string { + switch r { + case BalanceChangeUnspecified: + return "BalanceChangeUnspecified" + case BalanceIncreaseRewardMineUncle: + return "BalanceIncreaseRewardMineUncle" + case BalanceIncreaseRewardMineBlock: + return "BalanceIncreaseRewardMineBlock" + case BalanceIncreaseWithdrawal: + return "BalanceIncreaseWithdrawal" + case BalanceIncreaseGenesisBalance: + return "BalanceIncreaseGenesisBalance" + case BalanceIncreaseRewardTransactionFee: + return "BalanceIncreaseRewardTransactionFee" + case BalanceDecreaseGasBuy: + return "BalanceDecreaseGasBuy" + case BalanceIncreaseGasReturn: + return "BalanceIncreaseGasReturn" + case BalanceIncreaseDaoContract: + return "BalanceIncreaseDaoContract" + case BalanceDecreaseDaoAccount: + return "BalanceDecreaseDaoAccount" + case BalanceChangeTransfer: + return "BalanceChangeTransfer" + case BalanceChangeTouchAccount: + return "BalanceChangeTouchAccount" + case BalanceIncreaseSelfdestruct: + return "BalanceIncreaseSelfdestruct" + case BalanceDecreaseSelfdestruct: + return "BalanceDecreaseSelfdestruct" + case BalanceDecreaseSelfdestructBurn: + return "BalanceDecreaseSelfdestructBurn" + default: + return "unknown" + } +} + // GasChangeReason is used to indicate the reason for a gas change, useful // for tracing and reporting. // From a4117a8337e817b5ebdc855cab47307847905dc4 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Tue, 26 Mar 2024 11:30:50 +0200 Subject: [PATCH 02/28] add supply delta live tracer --- eth/tracers/internal/tracetest/supply_test.go | 567 ++++++++++++++++++ eth/tracers/live/supply.go | 239 ++++++++ 2 files changed, 806 insertions(+) create mode 100644 eth/tracers/internal/tracetest/supply_test.go create mode 100644 eth/tracers/live/supply.go diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go new file mode 100644 index 000000000000..a0427e747828 --- /dev/null +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -0,0 +1,567 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package tracetest + +import ( + "bufio" + "encoding/json" + "fmt" + "math/big" + "os" + "path" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/live" + "github.com/ethereum/go-ethereum/params" + + // Force-load live packages, to trigger registration + _ "github.com/ethereum/go-ethereum/eth/tracers/live" +) + +func emptyBlockGenerationFunc(b *core.BlockGen) {} + +func TestSupplyGenesisAlloc(t *testing.T) { + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + + config = *params.AllEthashProtocolChanges + + gspec = &core.Genesis{ + Config: &config, + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + addr2: {Balance: eth1}, + }, + } + ) + + expected := live.SupplyInfo{ + Delta: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), + Reward: common.Big0, + Withdrawals: common.Big0, + Burn: common.Big0, + Number: 0, + Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), + ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + } + + out, _, err := testSupplyTracer(gspec, emptyBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + actual := out[expected.Number] + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) + } +} + +func TestSupplyRewards(t *testing.T) { + var ( + config = *params.AllEthashProtocolChanges + + gspec = &core.Genesis{ + Config: &config, + } + ) + + expected := live.SupplyInfo{ + Delta: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), + Reward: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), + Withdrawals: common.Big0, + Burn: common.Big0, + Number: 1, + Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), + ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), + } + + out, _, err := testSupplyTracer(gspec, emptyBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + actual := out[expected.Number] + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) + } +} + +func TestSupplyEip1559Burn(t *testing.T) { + var ( + config = *params.AllEthashProtocolChanges + + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + // A sender who makes transactions, has some eth1 + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + + gspec = &core.Genesis{ + Config: &config, + BaseFee: big.NewInt(params.InitialBaseFee), + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + }, + } + ) + + signer := types.LatestSigner(gspec.Config) + + eip1559BlockGenerationFunc := func(b *core.BlockGen) { + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 0, + To: &aa, + Gas: 21000, + GasFeeCap: gwei5, + GasTipCap: big.NewInt(2), + } + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + } + + out, chain, err := testSupplyTracer(gspec, eip1559BlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + var ( + head = chain.CurrentBlock() + reward = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) + burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) + expected = live.SupplyInfo{ + Delta: new(big.Int).Sub(reward, burn), + Reward: reward, + Withdrawals: common.Big0, + Burn: burn, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + ) + + actual := out[expected.Number] + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) + } +} + +func TestSupplyWithdrawals(t *testing.T) { + var ( + config = *params.MergedTestChainConfig + gspec = &core.Genesis{ + Config: &config, + } + ) + + withdrawalsBlockGenerationFunc := func(b *core.BlockGen) { + b.SetPoS() + + b.AddWithdrawal(&types.Withdrawal{ + Validator: 42, + Address: common.Address{0xee}, + Amount: 1337, + }) + } + + out, chain, err := testSupplyTracer(gspec, withdrawalsBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + var ( + head = chain.CurrentBlock() + expected = live.SupplyInfo{ + Delta: big.NewInt(1337000000000), + Reward: common.Big0, + Withdrawals: big.NewInt(1337000000000), + Burn: common.Big0, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + actual = out[expected.Number] + ) + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) + } +} + +// Tests fund retrieval after contract's selfdestruct. +// Contract A calls contract B which selfdestructs, but B receives eth1 +// after the selfdestruct opcode executes from Contract A. +// Because Contract B is removed only at the end of the transaction +// the ether sent in between is burnt before Cancun hard fork. +func TestSupplySelfdestruct(t *testing.T) { + var ( + config = *params.TestChainConfig + + aa = common.HexToAddress("0x1111111111111111111111111111111111111111") + bb = common.HexToAddress("0x2222222222222222222222222222222222222222") + dad = common.HexToAddress("0x0000000000000000000000000000000000000dad") + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + + gspec = &core.Genesis{ + Config: &config, + BaseFee: big.NewInt(params.InitialBaseFee), + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + aa: { + Code: common.FromHex("0x61face60f01b6000527322222222222222222222222222222222222222226000806002600080855af160008103603457600080fd5b60008060008034865af1905060008103604c57600080fd5b5050"), + // Nonce: 0, + Balance: big.NewInt(0), + }, + bb: { + Code: common.FromHex("0x6000357fface000000000000000000000000000000000000000000000000000000000000808203602f57610dad80ff5b5050"), + Nonce: 0, + Balance: eth1, + }, + }, + } + ) + + gspec.Config.TerminalTotalDifficulty = big.NewInt(0) + + signer := types.LatestSigner(gspec.Config) + + testBlockGenerationFunc := func(b *core.BlockGen) { + b.SetPoS() + + txdata := &types.LegacyTx{ + Nonce: 0, + To: &aa, + Value: gwei5, + Gas: 150000, + GasPrice: gwei5, + Data: []byte{}, + } + + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + } + + // 1. Test pre Cancun + preCancunOutput, preCancunChain, err := testSupplyTracer(gspec, testBlockGenerationFunc) + if err != nil { + t.Fatalf("Pre-cancun failed to test supply tracer: %v", err) + } + + // Check balance at state: + // 1. 0x0000...000dad has 1 ether + // 2. A has 0 ether + // 3. B has 0 ether + statedb, _ := preCancunChain.State() + if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 { + t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", dad, got, exp) + } + if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 { + t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", aa, got, exp) + } + if got, exp := statedb.GetBalance(bb), big.NewInt(0); got.CmpBig(exp) != 0 { + t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + + head := preCancunChain.CurrentBlock() + // Check live trace output + expected := live.SupplyInfo{ + Delta: big.NewInt(-55294500000000), + Reward: common.Big0, + Withdrawals: common.Big0, + Burn: big.NewInt(55294500000000), + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + + actual := preCancunOutput[expected.Number] + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Pre-cancun incorrect supply info: expected %+v, got %+v", expected, actual) + } + + // 2. Test post Cancun + cancunTime := uint64(0) + gspec.Config.ShanghaiTime = &cancunTime + gspec.Config.CancunTime = &cancunTime + + postCancunOutput, postCancunChain, err := testSupplyTracer(gspec, testBlockGenerationFunc) + if err != nil { + t.Fatalf("Post-cancun failed to test supply tracer: %v", err) + } + + // Check balance at state: + // 1. 0x0000...000dad has 1 ether + // 3. A has 0 ether + // 3. B has 5 gwei + statedb, _ = postCancunChain.State() + if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 { + t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", dad, got, exp) + } + if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 { + t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", aa, got, exp) + } + if got, exp := statedb.GetBalance(bb), gwei5; got.CmpBig(exp) != 0 { + t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + + // Check live trace output + head = postCancunChain.CurrentBlock() + expected = live.SupplyInfo{ + Delta: big.NewInt(-55289500000000), + Reward: common.Big0, + Withdrawals: common.Big0, + Burn: big.NewInt(55289500000000), + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + + actual = postCancunOutput[expected.Number] + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Post-cancun incorrect supply info: expected %+v, got %+v", expected, actual) + } +} + +// Tests selfdestructing contract to send its balance to itself (burn). +// It tests both cases of selfdestructing succeding and being reverted. +// - Contract A calls B and D. +// - Contract B selfdestructs and sends the eth1 to itself (Burn amount to be counted). +// - Contract C selfdestructs and sends the eth1 to itself. +// - Contract D calls C and reverts (Burn amount of C +// has to be reverted as well). +func TestSupplySelfdestructItselfAndRevert(t *testing.T) { + var ( + config = *params.TestChainConfig + + aa = common.HexToAddress("0x1111111111111111111111111111111111111111") + bb = common.HexToAddress("0x2222222222222222222222222222222222222222") + cc = common.HexToAddress("0x3333333333333333333333333333333333333333") + dd = common.HexToAddress("0x4444444444444444444444444444444444444444") + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + eth2 = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) + eth5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.Ether)) + + gspec = &core.Genesis{ + Config: &config, + // BaseFee: big.NewInt(params.InitialBaseFee), + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + aa: { + // Contract code in YUL: + // + // object "ContractA" { + // code { + // let B := 0x2222222222222222222222222222222222222222 + // let D := 0x4444444444444444444444444444444444444444 + + // // Call to Contract B + // let resB:= call(gas(), B, 0, 0x0, 0x0, 0, 0) + + // // Call to Contract D + // let resD := call(gas(), D, 0, 0x0, 0x0, 0, 0) + // } + // } + Code: common.FromHex("0x73222222222222222222222222222222222222222273444444444444444444444444444444444444444460006000600060006000865af160006000600060006000865af150505050"), + Balance: common.Big0, + }, + bb: { + // Contract code in YUL: + // + // object "ContractB" { + // code { + // let self := address() + // selfdestruct(self) + // } + // } + Code: common.FromHex("0x3080ff50"), + Balance: eth5, + }, + cc: { + Code: common.FromHex("0x3080ff50"), + Balance: eth1, + }, + dd: { + // Contract code in YUL: + // + // object "ContractD" { + // code { + // let C := 0x3333333333333333333333333333333333333333 + + // // Call to Contract C + // let resC := call(gas(), C, 0, 0x0, 0x0, 0, 0) + + // // Revert + // revert(0, 0) + // } + // } + Code: common.FromHex("0x73333333333333333333333333333333333333333360006000600060006000855af160006000fd5050"), + Balance: eth2, + }, + }, + } + ) + + gspec.Config.TerminalTotalDifficulty = big.NewInt(0) + + signer := types.LatestSigner(gspec.Config) + + testBlockGenerationFunc := func(b *core.BlockGen) { + b.SetPoS() + + txdata := &types.LegacyTx{ + Nonce: 0, + To: &aa, + Value: common.Big0, + Gas: 150000, + GasPrice: gwei5, + Data: []byte{}, + } + + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + } + + output, chain, err := testSupplyTracer(gspec, testBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + // Check balance at state: + // 1. A has 0 ether + // 2. B has 0 ether, burned + // 3. C has 2 ether, selfdestructed but parent D reverted + // 4. D has 1 ether, reverted + statedb, _ := chain.State() + if got, exp := statedb.GetBalance(aa), common.Big0; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", aa, got, exp) + } + if got, exp := statedb.GetBalance(bb), common.Big0; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + if got, exp := statedb.GetBalance(cc), eth1; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + if got, exp := statedb.GetBalance(dd), eth2; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + + // Check live trace output + block := chain.GetBlockByNumber(1) + blockBurn := new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed()))) + totalBurn := new(big.Int).Add(blockBurn, eth5) // 5ETH burned from contract B + + expected := live.SupplyInfo{ + Delta: new(big.Int).Neg(totalBurn), + Reward: common.Big0, + Withdrawals: common.Big0, + Burn: totalBurn, + Number: 1, + Hash: block.Hash(), + ParentHash: block.ParentHash(), + } + + actual := output[expected.Number] + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) + } +} + +func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.SupplyInfo, *core.BlockChain, error) { + var ( + engine = beacon.New(ethash.NewFaker()) + ) + + traceOutputPath := os.TempDir() + traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") + defer os.Remove(traceOutputFilename) + + // Load supply tracer + tracer, err := tracers.LiveDirectory.New("supply", []byte(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) + if err != nil { + return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) + } + + chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, engine, vm.Config{Tracer: tracer}, nil, nil) + if err != nil { + return nil, nil, fmt.Errorf("failed to create tester chain: %v", err) + } + defer chain.Stop() + + _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, 1, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{1}) + gen(b) + }) + + if n, err := chain.InsertChain(blocks); err != nil { + return nil, chain, fmt.Errorf("block %d: failed to insert into chain: %v", n, err) + } + + // Check and compare the results + // TODO: replace file to pass results + file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666) + if err != nil { + return nil, chain, fmt.Errorf("failed to open output file: %v", err) + } + defer file.Close() + + var output []live.SupplyInfo + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + blockBytes := scanner.Bytes() + + var info live.SupplyInfo + if err := json.Unmarshal(blockBytes, &info); err != nil { + return nil, chain, fmt.Errorf("failed to unmarshal result: %v", err) + } + + output = append(output, info) + } + + return output, chain, nil +} diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go new file mode 100644 index 000000000000..bc17c01eb384 --- /dev/null +++ b/eth/tracers/live/supply.go @@ -0,0 +1,239 @@ +package live + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "math/big" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "gopkg.in/natefinch/lumberjack.v2" +) + +func init() { + tracers.LiveDirectory.Register("supply", newSupply) +} + +type SupplyInfo struct { + Delta *big.Int `json:"delta"` + Reward *big.Int `json:"reward"` + Withdrawals *big.Int `json:"withdrawals"` + Burn *big.Int `json:"burn"` + + // Block info + Number uint64 `json:"blockNumber"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` +} + +func (s *SupplyInfo) burn(amount *big.Int) { + s.Burn.Add(s.Burn, amount) + s.Delta.Sub(s.Delta, amount) +} + +type supplyTxCallstack struct { + calls []supplyTxCallstack + burn *big.Int +} + +type Supply struct { + delta SupplyInfo + txCallstack []supplyTxCallstack // Callstack for current transaction + logger *log.Logger +} + +type supplyTracerConfig struct { + Path string `json:"path"` // Path to the directory where the tracer logs will be stored + MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. +} + +func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { + var config supplyTracerConfig + if cfg != nil { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, fmt.Errorf("failed to parse config: %v", err) + } + } + + if config.Path == "" { + return nil, errors.New("supply tracer output path is required") + } + + // Store traces in a rotating file + loggerOutput := &lumberjack.Logger{ + Filename: filepath.Join(config.Path, "supply.jsonl"), + } + + if config.MaxSize > 0 { + loggerOutput.MaxSize = config.MaxSize + } + + logger := log.New(loggerOutput, "", 0) + + supplyInfo := newSupplyInfo() + + t := &Supply{ + delta: supplyInfo, + logger: logger, + } + return &tracing.Hooks{ + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnGenesisBlock: t.OnGenesisBlock, + OnTxStart: t.OnTxStart, + OnBalanceChange: t.OnBalanceChange, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + }, nil +} + +func newSupplyInfo() SupplyInfo { + return SupplyInfo{ + Delta: big.NewInt(0), + Reward: big.NewInt(0), + Withdrawals: big.NewInt(0), + Burn: big.NewInt(0), + + Number: 0, + Hash: common.Hash{}, + ParentHash: common.Hash{}, + } +} + +func (s *Supply) resetDelta() { + s.delta = newSupplyInfo() +} + +func (s *Supply) OnBlockStart(ev tracing.BlockEvent) { + s.resetDelta() + + s.delta.Number = ev.Block.NumberU64() + s.delta.Hash = ev.Block.Hash() + s.delta.ParentHash = ev.Block.ParentHash() + + // Calculate Burn for this block + if ev.Block.BaseFee() != nil { + burn := new(big.Int).Mul(new(big.Int).SetUint64(ev.Block.GasUsed()), ev.Block.BaseFee()) + s.delta.burn(burn) + } + // Blob burnt gas + if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil { + var ( + excess = *ev.Block.ExcessBlobGas() + baseFee = eip4844.CalcBlobFee(excess) + burn = new(big.Int).Mul(new(big.Int).SetUint64(*blobGas), baseFee) + ) + s.delta.burn(burn) + } +} + +func (s *Supply) OnBlockEnd(err error) { + out, _ := json.Marshal(s.delta) + s.logger.Println(string(out)) +} + +func (s *Supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { + s.resetDelta() + + s.delta.Number = b.NumberU64() + s.delta.Hash = b.Hash() + s.delta.ParentHash = b.ParentHash() + + // Initialize supply with total allocation in genesis block + for _, account := range alloc { + s.delta.Delta.Add(s.delta.Delta, account.Balance) + } + + out, _ := json.Marshal(s.delta) + s.logger.Println(string(out)) +} + +func (s *Supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { + diff := new(big.Int).Sub(newBalance, prevBalance) + + // NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock + switch reason { + case tracing.BalanceIncreaseRewardMineUncle: + case tracing.BalanceIncreaseRewardMineBlock: + s.delta.Reward.Add(s.delta.Reward, diff) + case tracing.BalanceIncreaseWithdrawal: + s.delta.Withdrawals.Add(s.delta.Withdrawals, diff) + case tracing.BalanceDecreaseSelfdestructBurn: + // BalanceDecreaseSelfdestructBurn is non-reversible as it happens + // at the end of the transaction. + s.delta.Burn.Sub(s.delta.Burn, diff) + default: + // fmt.Printf("~~\tNo need to take action. Change reason: %v\n\n", reason) + return + } + + s.delta.Delta.Add(s.delta.Delta, diff) +} + +func (s *Supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { + s.txCallstack = make([]supplyTxCallstack, 0, 1) +} + +// interalTxsHandler handles internal transactions burned amount +func (s *Supply) interalTxsHandler(call *supplyTxCallstack) { + // Handle Burned amount + if call.burn != nil { + s.delta.burn(call.burn) + } + + if len(call.calls) > 0 { + // Recursivelly handle internal calls + for _, call := range call.calls { + callCopy := call + s.interalTxsHandler(&callCopy) + } + } +} + +func (s *Supply) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + call := supplyTxCallstack{ + calls: make([]supplyTxCallstack, 0), + } + + // This is a special case of burned amount which has to be handled here + // which happens when type == selfdestruct and from == to. + if vm.OpCode(typ) == vm.SELFDESTRUCT && from == to && value.Cmp(common.Big0) == 1 { + call.burn = value + } + + // Append call to the callstack, so we can fill the details in CaptureExit + s.txCallstack = append(s.txCallstack, call) +} + +func (s *Supply) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth == 0 { + // No need to handle Burned amount if transaction is reverted + if !reverted { + s.interalTxsHandler(&s.txCallstack[0]) + } + return + } + + size := len(s.txCallstack) + if size <= 1 { + return + } + // Pop call + call := s.txCallstack[size-1] + s.txCallstack = s.txCallstack[:size-1] + size -= 1 + + // In case of a revert, we can drop the call and all its subcalls. + // Caution, that this has to happen after popping the call from the stack. + if reverted { + return + } + s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call) +} From 55a9f40b4e7c2eceea34bd7a6baeac865a423db3 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Tue, 26 Mar 2024 20:00:35 +0200 Subject: [PATCH 03/28] change `[]byte` to `json.RawMessage` in test --- eth/tracers/internal/tracetest/supply_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index a0427e747828..e58eb3b74f3c 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -521,7 +521,7 @@ func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.S defer os.Remove(traceOutputFilename) // Load supply tracer - tracer, err := tracers.LiveDirectory.New("supply", []byte(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) + tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) if err != nil { return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) } From 5db20834f065fb15ba4c4dc9927493572382b204 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Wed, 27 Mar 2024 10:50:42 +0200 Subject: [PATCH 04/28] fix temp path on Windows by converting backslashes to slashes --- eth/tracers/internal/tracetest/supply_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index e58eb3b74f3c..d9834d398ea1 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -23,6 +23,7 @@ import ( "math/big" "os" "path" + "path/filepath" "reflect" "testing" @@ -516,7 +517,7 @@ func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.S engine = beacon.New(ethash.NewFaker()) ) - traceOutputPath := os.TempDir() + traceOutputPath := filepath.ToSlash(os.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") defer os.Remove(traceOutputFilename) From bc983e2afd5f8520dc2448b4ddf989cc102e5973 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Wed, 27 Mar 2024 14:00:20 +0200 Subject: [PATCH 05/28] allow test to use different file for tracer output --- eth/tracers/internal/tracetest/supply_test.go | 13 ++++++++----- eth/tracers/live/supply.go | 11 ++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index d9834d398ea1..5578d8ddb03b 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -22,7 +22,6 @@ import ( "fmt" "math/big" "os" - "path" "path/filepath" "reflect" "testing" @@ -518,11 +517,15 @@ func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.S ) traceOutputPath := filepath.ToSlash(os.TempDir()) - traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") - defer os.Remove(traceOutputFilename) + traceOutputFile, err := os.CreateTemp(traceOutputPath, "supply-*.jsonl") + traceOutputFilename := filepath.Base(traceOutputFile.Name()) + if err != nil { + return nil, nil, fmt.Errorf("failed to create temp file for supply logs: %v", err) + } + defer os.Remove(traceOutputFile.Name()) // Load supply tracer - tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) + tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s","filename":"%s"}`, traceOutputPath, traceOutputFilename))) if err != nil { return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) } @@ -544,7 +547,7 @@ func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.S // Check and compare the results // TODO: replace file to pass results - file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666) + file, err := os.OpenFile(traceOutputFile.Name(), os.O_RDONLY, 0666) if err != nil { return nil, chain, fmt.Errorf("failed to open output file: %v", err) } diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index bc17c01eb384..3b5fa9d98785 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -50,8 +50,9 @@ type Supply struct { } type supplyTracerConfig struct { - Path string `json:"path"` // Path to the directory where the tracer logs will be stored - MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. + Path string `json:"path"` // Path to the directory where the tracer logs will be stored + Filename string `json:"filename"` // Filename of the tracer log file. Defaults to "supply.jsonl". + MaxSize int `json:"maxsize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. } func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { @@ -66,9 +67,13 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { return nil, errors.New("supply tracer output path is required") } + if config.Filename == "" { + config.Filename = "supply.jsonl" + } + // Store traces in a rotating file loggerOutput := &lumberjack.Logger{ - Filename: filepath.Join(config.Path, "supply.jsonl"), + Filename: filepath.Join(config.Path, config.Filename), } if config.MaxSize > 0 { From 3a6cf0a4a00399dda0b9ac2b5c5e39a6909559d2 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 27 Mar 2024 16:22:32 +0100 Subject: [PATCH 06/28] Revert "allow test to use different file for tracer output" This reverts commit bc983e2afd5f8520dc2448b4ddf989cc102e5973. --- eth/tracers/internal/tracetest/supply_test.go | 13 +++++-------- eth/tracers/live/supply.go | 11 +++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 5578d8ddb03b..d9834d398ea1 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -22,6 +22,7 @@ import ( "fmt" "math/big" "os" + "path" "path/filepath" "reflect" "testing" @@ -517,15 +518,11 @@ func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.S ) traceOutputPath := filepath.ToSlash(os.TempDir()) - traceOutputFile, err := os.CreateTemp(traceOutputPath, "supply-*.jsonl") - traceOutputFilename := filepath.Base(traceOutputFile.Name()) - if err != nil { - return nil, nil, fmt.Errorf("failed to create temp file for supply logs: %v", err) - } - defer os.Remove(traceOutputFile.Name()) + traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") + defer os.Remove(traceOutputFilename) // Load supply tracer - tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s","filename":"%s"}`, traceOutputPath, traceOutputFilename))) + tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) if err != nil { return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) } @@ -547,7 +544,7 @@ func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.S // Check and compare the results // TODO: replace file to pass results - file, err := os.OpenFile(traceOutputFile.Name(), os.O_RDONLY, 0666) + file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666) if err != nil { return nil, chain, fmt.Errorf("failed to open output file: %v", err) } diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 3b5fa9d98785..bc17c01eb384 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -50,9 +50,8 @@ type Supply struct { } type supplyTracerConfig struct { - Path string `json:"path"` // Path to the directory where the tracer logs will be stored - Filename string `json:"filename"` // Filename of the tracer log file. Defaults to "supply.jsonl". - MaxSize int `json:"maxsize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. + Path string `json:"path"` // Path to the directory where the tracer logs will be stored + MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. } func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { @@ -67,13 +66,9 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { return nil, errors.New("supply tracer output path is required") } - if config.Filename == "" { - config.Filename = "supply.jsonl" - } - // Store traces in a rotating file loggerOutput := &lumberjack.Logger{ - Filename: filepath.Join(config.Path, config.Filename), + Filename: filepath.Join(config.Path, "supply.jsonl"), } if config.MaxSize > 0 { From 53f61525f25657e31f9e213db271894249f60c2c Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 27 Mar 2024 16:26:02 +0100 Subject: [PATCH 07/28] try alternative tempdir --- eth/tracers/internal/tracetest/supply_test.go | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index d9834d398ea1..559bc2c98aad 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -23,7 +23,6 @@ import ( "math/big" "os" "path" - "path/filepath" "reflect" "testing" @@ -74,7 +73,7 @@ func TestSupplyGenesisAlloc(t *testing.T) { ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), } - out, _, err := testSupplyTracer(gspec, emptyBlockGenerationFunc) + out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) if err != nil { t.Fatalf("failed to test supply tracer: %v", err) } @@ -105,7 +104,7 @@ func TestSupplyRewards(t *testing.T) { ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), } - out, _, err := testSupplyTracer(gspec, emptyBlockGenerationFunc) + out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) if err != nil { t.Fatalf("failed to test supply tracer: %v", err) } @@ -154,7 +153,7 @@ func TestSupplyEip1559Burn(t *testing.T) { b.AddTx(tx) } - out, chain, err := testSupplyTracer(gspec, eip1559BlockGenerationFunc) + out, chain, err := testSupplyTracer(t, gspec, eip1559BlockGenerationFunc) if err != nil { t.Fatalf("failed to test supply tracer: %v", err) } @@ -197,7 +196,7 @@ func TestSupplyWithdrawals(t *testing.T) { }) } - out, chain, err := testSupplyTracer(gspec, withdrawalsBlockGenerationFunc) + out, chain, err := testSupplyTracer(t, gspec, withdrawalsBlockGenerationFunc) if err != nil { t.Fatalf("failed to test supply tracer: %v", err) } @@ -280,7 +279,7 @@ func TestSupplySelfdestruct(t *testing.T) { } // 1. Test pre Cancun - preCancunOutput, preCancunChain, err := testSupplyTracer(gspec, testBlockGenerationFunc) + preCancunOutput, preCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) if err != nil { t.Fatalf("Pre-cancun failed to test supply tracer: %v", err) } @@ -323,7 +322,7 @@ func TestSupplySelfdestruct(t *testing.T) { gspec.Config.ShanghaiTime = &cancunTime gspec.Config.CancunTime = &cancunTime - postCancunOutput, postCancunChain, err := testSupplyTracer(gspec, testBlockGenerationFunc) + postCancunOutput, postCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) if err != nil { t.Fatalf("Post-cancun failed to test supply tracer: %v", err) } @@ -466,7 +465,7 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { b.AddTx(tx) } - output, chain, err := testSupplyTracer(gspec, testBlockGenerationFunc) + output, chain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) if err != nil { t.Fatalf("failed to test supply tracer: %v", err) } @@ -512,14 +511,13 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { } } -func testSupplyTracer(genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.SupplyInfo, *core.BlockChain, error) { +func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.SupplyInfo, *core.BlockChain, error) { var ( engine = beacon.New(ethash.NewFaker()) ) - traceOutputPath := filepath.ToSlash(os.TempDir()) + traceOutputPath := t.TempDir() traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") - defer os.Remove(traceOutputFilename) // Load supply tracer tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) From 9e21e2dd2ee6e109688004d1583b63bdbc3280bb Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 28 Mar 2024 13:59:33 +0200 Subject: [PATCH 08/28] use stringer to generate BalanceChangeReason String method https://pkg.go.dev/golang.org/x/tools/cmd/stringer --- .../gen_balance_change_reason_stringer.go | 37 +++++++++++++++++ core/tracing/hooks.go | 40 +------------------ 2 files changed, 39 insertions(+), 38 deletions(-) create mode 100644 core/tracing/gen_balance_change_reason_stringer.go diff --git a/core/tracing/gen_balance_change_reason_stringer.go b/core/tracing/gen_balance_change_reason_stringer.go new file mode 100644 index 000000000000..d3a515a12d37 --- /dev/null +++ b/core/tracing/gen_balance_change_reason_stringer.go @@ -0,0 +1,37 @@ +// Code generated by "stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go"; DO NOT EDIT. + +package tracing + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BalanceChangeUnspecified-0] + _ = x[BalanceIncreaseRewardMineUncle-1] + _ = x[BalanceIncreaseRewardMineBlock-2] + _ = x[BalanceIncreaseWithdrawal-3] + _ = x[BalanceIncreaseGenesisBalance-4] + _ = x[BalanceIncreaseRewardTransactionFee-5] + _ = x[BalanceDecreaseGasBuy-6] + _ = x[BalanceIncreaseGasReturn-7] + _ = x[BalanceIncreaseDaoContract-8] + _ = x[BalanceDecreaseDaoAccount-9] + _ = x[BalanceChangeTransfer-10] + _ = x[BalanceChangeTouchAccount-11] + _ = x[BalanceIncreaseSelfdestruct-12] + _ = x[BalanceDecreaseSelfdestruct-13] + _ = x[BalanceDecreaseSelfdestructBurn-14] +} + +const _BalanceChangeReason_name = "BalanceChangeUnspecifiedBalanceIncreaseRewardMineUncleBalanceIncreaseRewardMineBlockBalanceIncreaseWithdrawalBalanceIncreaseGenesisBalanceBalanceIncreaseRewardTransactionFeeBalanceDecreaseGasBuyBalanceIncreaseGasReturnBalanceIncreaseDaoContractBalanceDecreaseDaoAccountBalanceChangeTransferBalanceChangeTouchAccountBalanceIncreaseSelfdestructBalanceDecreaseSelfdestructBalanceDecreaseSelfdestructBurn" + +var _BalanceChangeReason_index = [...]uint16{0, 24, 54, 84, 109, 138, 173, 194, 218, 244, 269, 290, 315, 342, 369, 400} + +func (i BalanceChangeReason) String() string { + if i >= BalanceChangeReason(len(_BalanceChangeReason_index)-1) { + return "BalanceChangeReason(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BalanceChangeReason_name[_BalanceChangeReason_index[i]:_BalanceChangeReason_index[i+1]] +} diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 406d34f9da40..5d7bb6891bea 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -169,6 +169,8 @@ type Hooks struct { // for tracing and reporting. type BalanceChangeReason byte +//go:generate stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go + const ( BalanceChangeUnspecified BalanceChangeReason = 0 @@ -214,44 +216,6 @@ const ( BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 ) -// String returns a string representation of the reason. -func (r BalanceChangeReason) String() string { - switch r { - case BalanceChangeUnspecified: - return "BalanceChangeUnspecified" - case BalanceIncreaseRewardMineUncle: - return "BalanceIncreaseRewardMineUncle" - case BalanceIncreaseRewardMineBlock: - return "BalanceIncreaseRewardMineBlock" - case BalanceIncreaseWithdrawal: - return "BalanceIncreaseWithdrawal" - case BalanceIncreaseGenesisBalance: - return "BalanceIncreaseGenesisBalance" - case BalanceIncreaseRewardTransactionFee: - return "BalanceIncreaseRewardTransactionFee" - case BalanceDecreaseGasBuy: - return "BalanceDecreaseGasBuy" - case BalanceIncreaseGasReturn: - return "BalanceIncreaseGasReturn" - case BalanceIncreaseDaoContract: - return "BalanceIncreaseDaoContract" - case BalanceDecreaseDaoAccount: - return "BalanceDecreaseDaoAccount" - case BalanceChangeTransfer: - return "BalanceChangeTransfer" - case BalanceChangeTouchAccount: - return "BalanceChangeTouchAccount" - case BalanceIncreaseSelfdestruct: - return "BalanceIncreaseSelfdestruct" - case BalanceDecreaseSelfdestruct: - return "BalanceDecreaseSelfdestruct" - case BalanceDecreaseSelfdestructBurn: - return "BalanceDecreaseSelfdestructBurn" - default: - return "unknown" - } -} - // GasChangeReason is used to indicate the reason for a gas change, useful // for tracing and reporting. // From a6cabe11b08052f9d88e0dee67a7579e09492fed Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 28 Mar 2024 14:01:58 +0200 Subject: [PATCH 09/28] fix failing test --- eth/tracers/internal/tracetest/supply_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 559bc2c98aad..9b6100267bd7 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -23,6 +23,7 @@ import ( "math/big" "os" "path" + "path/filepath" "reflect" "testing" @@ -516,7 +517,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG engine = beacon.New(ethash.NewFaker()) ) - traceOutputPath := t.TempDir() + traceOutputPath := filepath.ToSlash(t.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") // Load supply tracer From df69332109bcca64f165093428ec6439eef68fa2 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 28 Mar 2024 20:02:59 +0200 Subject: [PATCH 10/28] make supply type private --- eth/tracers/live/supply.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index bc17c01eb384..40a351c32584 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -43,8 +43,8 @@ type supplyTxCallstack struct { burn *big.Int } -type Supply struct { delta SupplyInfo +type supply struct { txCallstack []supplyTxCallstack // Callstack for current transaction logger *log.Logger } @@ -79,7 +79,7 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { supplyInfo := newSupplyInfo() - t := &Supply{ + t := &supply{ delta: supplyInfo, logger: logger, } @@ -107,11 +107,11 @@ func newSupplyInfo() SupplyInfo { } } -func (s *Supply) resetDelta() { +func (s *supply) resetDelta() { s.delta = newSupplyInfo() } -func (s *Supply) OnBlockStart(ev tracing.BlockEvent) { +func (s *supply) OnBlockStart(ev tracing.BlockEvent) { s.resetDelta() s.delta.Number = ev.Block.NumberU64() @@ -134,12 +134,12 @@ func (s *Supply) OnBlockStart(ev tracing.BlockEvent) { } } -func (s *Supply) OnBlockEnd(err error) { +func (s *supply) OnBlockEnd(err error) { out, _ := json.Marshal(s.delta) s.logger.Println(string(out)) } -func (s *Supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { +func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.resetDelta() s.delta.Number = b.NumberU64() @@ -155,7 +155,7 @@ func (s *Supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.logger.Println(string(out)) } -func (s *Supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { +func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { diff := new(big.Int).Sub(newBalance, prevBalance) // NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock @@ -177,12 +177,12 @@ func (s *Supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big. s.delta.Delta.Add(s.delta.Delta, diff) } -func (s *Supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { s.txCallstack = make([]supplyTxCallstack, 0, 1) } // interalTxsHandler handles internal transactions burned amount -func (s *Supply) interalTxsHandler(call *supplyTxCallstack) { +func (s *supply) interalTxsHandler(call *supplyTxCallstack) { // Handle Burned amount if call.burn != nil { s.delta.burn(call.burn) @@ -197,7 +197,7 @@ func (s *Supply) interalTxsHandler(call *supplyTxCallstack) { } } -func (s *Supply) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (s *supply) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { call := supplyTxCallstack{ calls: make([]supplyTxCallstack, 0), } @@ -212,7 +212,7 @@ func (s *Supply) OnEnter(depth int, typ byte, from common.Address, to common.Add s.txCallstack = append(s.txCallstack, call) } -func (s *Supply) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { +func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { if depth == 0 { // No need to handle Burned amount if transaction is reverted if !reverted { From fb0160ba9ffcfee54d1f5ad9d3ad974ac29e342b Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 28 Mar 2024 20:03:33 +0200 Subject: [PATCH 11/28] make SupplyInfo private --- eth/tracers/internal/tracetest/supply_test.go | 33 ++++++++++++------- eth/tracers/live/supply.go | 10 +++--- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 9b6100267bd7..97843f59729b 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -36,13 +36,24 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers" - "github.com/ethereum/go-ethereum/eth/tracers/live" "github.com/ethereum/go-ethereum/params" // Force-load live packages, to trigger registration _ "github.com/ethereum/go-ethereum/eth/tracers/live" ) +type supplyInfo struct { + Delta *big.Int `json:"delta"` + Reward *big.Int `json:"reward"` + Withdrawals *big.Int `json:"withdrawals"` + Burn *big.Int `json:"burn"` + + // Block info + Number uint64 `json:"blockNumber"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` +} + func emptyBlockGenerationFunc(b *core.BlockGen) {} func TestSupplyGenesisAlloc(t *testing.T) { @@ -64,7 +75,7 @@ func TestSupplyGenesisAlloc(t *testing.T) { } ) - expected := live.SupplyInfo{ + expected := supplyInfo{ Delta: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), Reward: common.Big0, Withdrawals: common.Big0, @@ -95,7 +106,7 @@ func TestSupplyRewards(t *testing.T) { } ) - expected := live.SupplyInfo{ + expected := supplyInfo{ Delta: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), Reward: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), Withdrawals: common.Big0, @@ -162,7 +173,7 @@ func TestSupplyEip1559Burn(t *testing.T) { head = chain.CurrentBlock() reward = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) - expected = live.SupplyInfo{ + expected = supplyInfo{ Delta: new(big.Int).Sub(reward, burn), Reward: reward, Withdrawals: common.Big0, @@ -204,7 +215,7 @@ func TestSupplyWithdrawals(t *testing.T) { var ( head = chain.CurrentBlock() - expected = live.SupplyInfo{ + expected = supplyInfo{ Delta: big.NewInt(1337000000000), Reward: common.Big0, Withdrawals: big.NewInt(1337000000000), @@ -302,7 +313,7 @@ func TestSupplySelfdestruct(t *testing.T) { head := preCancunChain.CurrentBlock() // Check live trace output - expected := live.SupplyInfo{ + expected := supplyInfo{ Delta: big.NewInt(-55294500000000), Reward: common.Big0, Withdrawals: common.Big0, @@ -345,7 +356,7 @@ func TestSupplySelfdestruct(t *testing.T) { // Check live trace output head = postCancunChain.CurrentBlock() - expected = live.SupplyInfo{ + expected = supplyInfo{ Delta: big.NewInt(-55289500000000), Reward: common.Big0, Withdrawals: common.Big0, @@ -495,7 +506,7 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { blockBurn := new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed()))) totalBurn := new(big.Int).Add(blockBurn, eth5) // 5ETH burned from contract B - expected := live.SupplyInfo{ + expected := supplyInfo{ Delta: new(big.Int).Neg(totalBurn), Reward: common.Big0, Withdrawals: common.Big0, @@ -512,7 +523,7 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { } } -func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]live.SupplyInfo, *core.BlockChain, error) { +func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) { var ( engine = beacon.New(ethash.NewFaker()) ) @@ -549,13 +560,13 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG } defer file.Close() - var output []live.SupplyInfo + var output []supplyInfo scanner := bufio.NewScanner(file) for scanner.Scan() { blockBytes := scanner.Bytes() - var info live.SupplyInfo + var info supplyInfo if err := json.Unmarshal(blockBytes, &info); err != nil { return nil, chain, fmt.Errorf("failed to unmarshal result: %v", err) } diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 40a351c32584..1c2fdeb9f72d 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -21,7 +21,7 @@ func init() { tracers.LiveDirectory.Register("supply", newSupply) } -type SupplyInfo struct { +type supplyInfo struct { Delta *big.Int `json:"delta"` Reward *big.Int `json:"reward"` Withdrawals *big.Int `json:"withdrawals"` @@ -33,7 +33,7 @@ type SupplyInfo struct { ParentHash common.Hash `json:"parentHash"` } -func (s *SupplyInfo) burn(amount *big.Int) { +func (s *supplyInfo) burn(amount *big.Int) { s.Burn.Add(s.Burn, amount) s.Delta.Sub(s.Delta, amount) } @@ -43,8 +43,8 @@ type supplyTxCallstack struct { burn *big.Int } - delta SupplyInfo type supply struct { + delta supplyInfo txCallstack []supplyTxCallstack // Callstack for current transaction logger *log.Logger } @@ -94,8 +94,8 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { }, nil } -func newSupplyInfo() SupplyInfo { - return SupplyInfo{ +func newSupplyInfo() supplyInfo { + return supplyInfo{ Delta: big.NewInt(0), Reward: big.NewInt(0), Withdrawals: big.NewInt(0), From 6e7f5bcd0577480571217404f0043210b023f39f Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 28 Mar 2024 20:04:07 +0200 Subject: [PATCH 12/28] typo interalTxsHandler->internalTxsHandler --- eth/tracers/live/supply.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 1c2fdeb9f72d..f5b860f9c781 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -181,8 +181,8 @@ func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from co s.txCallstack = make([]supplyTxCallstack, 0, 1) } -// interalTxsHandler handles internal transactions burned amount -func (s *supply) interalTxsHandler(call *supplyTxCallstack) { +// internalTxsHandler handles internal transactions burned amount +func (s *supply) internalTxsHandler(call *supplyTxCallstack) { // Handle Burned amount if call.burn != nil { s.delta.burn(call.burn) @@ -192,7 +192,7 @@ func (s *supply) interalTxsHandler(call *supplyTxCallstack) { // Recursivelly handle internal calls for _, call := range call.calls { callCopy := call - s.interalTxsHandler(&callCopy) + s.internalTxsHandler(&callCopy) } } } @@ -216,7 +216,7 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev if depth == 0 { // No need to handle Burned amount if transaction is reverted if !reverted { - s.interalTxsHandler(&s.txCallstack[0]) + s.internalTxsHandler(&s.txCallstack[0]) } return } From 91260a7c292b92eae2de6e9911d9e36d2ddfb1d6 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 28 Mar 2024 20:09:00 +0200 Subject: [PATCH 13/28] =?UTF-8?q?try=20a=20fix=20for=20=E2=80=9CTempDir=20?= =?UTF-8?q?RemoveAll=20cleanup=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eth/tracers/internal/tracetest/supply_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 97843f59729b..e933576f7bc0 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -530,6 +530,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG traceOutputPath := filepath.ToSlash(t.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") + defer os.Remove(traceOutputFilename) // Load supply tracer tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) From 4320189830090d07bd8dfb4258ce769a24e1524f Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Fri, 29 Mar 2024 13:01:41 +0200 Subject: [PATCH 14/28] try a fix for test to pass on Windows We try to solve the following error: ``` --- FAIL: TestSupplySelfdestruct (1.56s) testing.go:1231: TempDir RemoveAll cleanup: remove C:\Users\appveyor\AppData\Local\Temp\1\TestSupplySelfdestruct3803137642\001\supply.jsonl: The process cannot access the file because it is being used by another process. ``` --- eth/tracers/internal/tracetest/supply_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index e933576f7bc0..9f4f9f692f30 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -530,13 +530,14 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG traceOutputPath := filepath.ToSlash(t.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") - defer os.Remove(traceOutputFilename) // Load supply tracer tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) if err != nil { return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) } + // hack so as the supply log file handler closes, for tests to work on windows + defer func() { tracer = nil }() chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, engine, vm.Config{Tracer: tracer}, nil, nil) if err != nil { From cba97ef4306176fc33db0e4983fcd43bdc11ce43 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Fri, 29 Mar 2024 16:19:06 +0200 Subject: [PATCH 15/28] try a fix for test to pass on Windows --- eth/tracers/internal/tracetest/supply_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 9f4f9f692f30..46e55f268dc4 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -528,16 +528,16 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG engine = beacon.New(ethash.NewFaker()) ) - traceOutputPath := filepath.ToSlash(t.TempDir()) + tmpDir := t.TempDir() + traceOutputPath := filepath.ToSlash(tmpDir) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") + t.Cleanup(func() { os.RemoveAll(tmpDir) }) // Load supply tracer tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) if err != nil { return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) } - // hack so as the supply log file handler closes, for tests to work on windows - defer func() { tracer = nil }() chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, engine, vm.Config{Tracer: tracer}, nil, nil) if err != nil { From 4708a193192b1a0577e1834c327c8a8738d457e3 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Fri, 29 Mar 2024 22:03:34 +0200 Subject: [PATCH 16/28] fix test by closing logger file Was expecting lumberjack.Rotate() to close the file --- eth/tracers/internal/tracetest/supply_test.go | 5 +---- eth/tracers/live/supply.go | 7 ++++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 46e55f268dc4..9942c166c02c 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -528,10 +528,8 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG engine = beacon.New(ethash.NewFaker()) ) - tmpDir := t.TempDir() - traceOutputPath := filepath.ToSlash(tmpDir) + traceOutputPath := filepath.ToSlash(t.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") - t.Cleanup(func() { os.RemoveAll(tmpDir) }) // Load supply tracer tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) @@ -555,7 +553,6 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG } // Check and compare the results - // TODO: replace file to pass results file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666) if err != nil { return nil, chain, fmt.Errorf("failed to open output file: %v", err) diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index f5b860f9c781..f35bd1d2be10 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -67,15 +67,16 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { } // Store traces in a rotating file - loggerOutput := &lumberjack.Logger{ + loggerOutputFile := &lumberjack.Logger{ Filename: filepath.Join(config.Path, "supply.jsonl"), } + defer loggerOutputFile.Close() if config.MaxSize > 0 { - loggerOutput.MaxSize = config.MaxSize + loggerOutputFile.MaxSize = config.MaxSize } - logger := log.New(loggerOutput, "", 0) + logger := log.New(loggerOutputFile, "", 0) supplyInfo := newSupplyInfo() From 00ef42b541913cdcb2264e02c21f0bd5f4081f0c Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Sat, 30 Mar 2024 19:18:05 +0200 Subject: [PATCH 17/28] add OnTerminate in Hooks which triggered on blockchain Stop --- core/blockchain.go | 4 ++++ core/tracing/hooks.go | 4 ++++ eth/tracers/live/supply.go | 18 ++++++++++++------ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 12fdcf72456c..b83288124ecd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1171,6 +1171,10 @@ func (bc *BlockChain) Stop() { if err := bc.triedb.Close(); err != nil { log.Error("Failed to close trie database", "err", err) } + // Call OnTerminate hook on Tracers if it is set. + if bc.vmConfig.Tracer != nil && bc.vmConfig.Tracer.OnTerminate != nil { + bc.vmConfig.Tracer.OnTerminate() + } log.Info("Blockchain stopped") } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 5d7bb6891bea..faf1ce4d9646 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -140,6 +140,9 @@ type ( // LogHook is called when a log is emitted. LogHook = func(log *types.Log) + + // TerminateHook is called when the tracer is terminated. + TerminateHook = func() ) type Hooks struct { @@ -163,6 +166,7 @@ type Hooks struct { OnCodeChange CodeChangeHook OnStorageChange StorageChangeHook OnLog LogHook + OnTerminate TerminateHook } // BalanceChangeReason is used to indicate the reason for a balance change, useful diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index f35bd1d2be10..a1b0724219f1 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -44,9 +44,10 @@ type supplyTxCallstack struct { } type supply struct { - delta supplyInfo - txCallstack []supplyTxCallstack // Callstack for current transaction - logger *log.Logger + delta supplyInfo + txCallstack []supplyTxCallstack // Callstack for current transaction + loggerOutputFile *lumberjack.Logger + logger *log.Logger } type supplyTracerConfig struct { @@ -70,7 +71,6 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { loggerOutputFile := &lumberjack.Logger{ Filename: filepath.Join(config.Path, "supply.jsonl"), } - defer loggerOutputFile.Close() if config.MaxSize > 0 { loggerOutputFile.MaxSize = config.MaxSize @@ -81,8 +81,9 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { supplyInfo := newSupplyInfo() t := &supply{ - delta: supplyInfo, - logger: logger, + delta: supplyInfo, + loggerOutputFile: loggerOutputFile, + logger: logger, } return &tracing.Hooks{ OnBlockStart: t.OnBlockStart, @@ -92,6 +93,7 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { OnBalanceChange: t.OnBalanceChange, OnEnter: t.OnEnter, OnExit: t.OnExit, + OnTerminate: t.OnTerminate, }, nil } @@ -238,3 +240,7 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev } s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call) } + +func (s *supply) OnTerminate() { + s.loggerOutputFile.Close() +} From de1c41d0e93584c3b577279b91614b27eac48255 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Sat, 30 Mar 2024 20:25:16 +0200 Subject: [PATCH 18/28] remove comment --- eth/tracers/live/supply.go | 1 - 1 file changed, 1 deletion(-) diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index a1b0724219f1..301492969641 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -173,7 +173,6 @@ func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big. // at the end of the transaction. s.delta.Burn.Sub(s.delta.Burn, diff) default: - // fmt.Printf("~~\tNo need to take action. Change reason: %v\n\n", reason) return } From 200f49607797cbf352d108b945e6a35c125c3315 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Sun, 31 Mar 2024 16:55:05 +0300 Subject: [PATCH 19/28] Update core/blockchain.go Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com> --- core/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/blockchain.go b/core/blockchain.go index b83288124ecd..bed78996bec3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1171,7 +1171,7 @@ func (bc *BlockChain) Stop() { if err := bc.triedb.Close(); err != nil { log.Error("Failed to close trie database", "err", err) } - // Call OnTerminate hook on Tracers if it is set. + // Allow tracers to clean-up and release resources. if bc.vmConfig.Tracer != nil && bc.vmConfig.Tracer.OnTerminate != nil { bc.vmConfig.Tracer.OnTerminate() } From f65a19af15fd43bda8b80484735001dd332791aa Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Sun, 31 Mar 2024 17:00:11 +0300 Subject: [PATCH 20/28] log any error during log file close --- eth/tracers/live/supply.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 301492969641..0b16f60d3dab 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -241,5 +241,7 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev } func (s *supply) OnTerminate() { - s.loggerOutputFile.Close() + if err := s.loggerOutputFile.Close(); err != nil { + log.Printf("failed to close supply tracer log file: %v", err) + } } From e67c3bfd620f630a986b2b5fe4eac1184d69ff00 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 1 Apr 2024 15:20:21 +0200 Subject: [PATCH 21/28] refactor logging --- eth/tracers/live/supply.go | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 0b16f60d3dab..f352be940fac 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "math/big" "path/filepath" @@ -14,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/log" "gopkg.in/natefinch/lumberjack.v2" ) @@ -44,10 +44,9 @@ type supplyTxCallstack struct { } type supply struct { - delta supplyInfo - txCallstack []supplyTxCallstack // Callstack for current transaction - loggerOutputFile *lumberjack.Logger - logger *log.Logger + delta supplyInfo + txCallstack []supplyTxCallstack // Callstack for current transaction + logger *lumberjack.Logger } type supplyTracerConfig struct { @@ -62,28 +61,21 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { return nil, fmt.Errorf("failed to parse config: %v", err) } } - if config.Path == "" { return nil, errors.New("supply tracer output path is required") } // Store traces in a rotating file - loggerOutputFile := &lumberjack.Logger{ + logger := &lumberjack.Logger{ Filename: filepath.Join(config.Path, "supply.jsonl"), } - if config.MaxSize > 0 { - loggerOutputFile.MaxSize = config.MaxSize + logger.MaxSize = config.MaxSize } - logger := log.New(loggerOutputFile, "", 0) - - supplyInfo := newSupplyInfo() - t := &supply{ - delta: supplyInfo, - loggerOutputFile: loggerOutputFile, - logger: logger, + delta: newSupplyInfo(), + logger: logger, } return &tracing.Hooks{ OnBlockStart: t.OnBlockStart, @@ -138,8 +130,7 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { } func (s *supply) OnBlockEnd(err error) { - out, _ := json.Marshal(s.delta) - s.logger.Println(string(out)) + s.write(s.delta) } func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { @@ -154,8 +145,7 @@ func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.delta.Delta.Add(s.delta.Delta, account.Balance) } - out, _ := json.Marshal(s.delta) - s.logger.Println(string(out)) + s.write(s.delta) } func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { @@ -241,7 +231,17 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev } func (s *supply) OnTerminate() { - if err := s.loggerOutputFile.Close(); err != nil { - log.Printf("failed to close supply tracer log file: %v", err) + if err := s.logger.Close(); err != nil { + log.Warn("failed to close supply tracer log file", "error", err) + } +} + +func (s *supply) write(data any) { + out, _ := json.Marshal(data) + if _, err := s.logger.Write(out); err != nil { + log.Warn("failed to write to supply tracer log file", "error", err) + } + if _, err := s.logger.Write([]byte{'\n'}); err != nil { + log.Warn("failed to write to supply tracer log file", "error", err) } } From 5ba5254cfbf426756eef1ec8df71cb8a32393e6f Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Wed, 3 Apr 2024 14:07:59 +0300 Subject: [PATCH 22/28] =?UTF-8?q?rename=20Hook=20=E2=80=9COnTerminate?= =?UTF-8?q?=E2=80=9D=20to=20=E2=80=9COnBlockchainTerminate=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/blockchain.go | 4 ++-- core/tracing/hooks.go | 18 +++++++++--------- eth/tracers/live/supply.go | 18 +++++++++--------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index bed78996bec3..564eef44e6ea 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1172,8 +1172,8 @@ func (bc *BlockChain) Stop() { log.Error("Failed to close trie database", "err", err) } // Allow tracers to clean-up and release resources. - if bc.vmConfig.Tracer != nil && bc.vmConfig.Tracer.OnTerminate != nil { - bc.vmConfig.Tracer.OnTerminate() + if bc.vmConfig.Tracer != nil && bc.vmConfig.Tracer.OnBlockchainTerminate != nil { + bc.vmConfig.Tracer.OnBlockchainTerminate() } log.Info("Blockchain stopped") } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index faf1ce4d9646..6da079c6f06e 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -107,6 +107,9 @@ type ( // BlockchainInitHook is called when the blockchain is initialized. BlockchainInitHook = func(chainConfig *params.ChainConfig) + // BlockchainTerminateHook is called when the tracer is terminated. + BlockchainTerminateHook = func() + // BlockStartHook is called before executing `block`. // `td` is the total difficulty prior to `block`. BlockStartHook = func(event BlockEvent) @@ -140,9 +143,6 @@ type ( // LogHook is called when a log is emitted. LogHook = func(log *types.Log) - - // TerminateHook is called when the tracer is terminated. - TerminateHook = func() ) type Hooks struct { @@ -155,18 +155,18 @@ type Hooks struct { OnFault FaultHook OnGasChange GasChangeHook // Chain events - OnBlockchainInit BlockchainInitHook - OnBlockStart BlockStartHook - OnBlockEnd BlockEndHook - OnSkippedBlock SkippedBlockHook - OnGenesisBlock GenesisBlockHook + OnBlockchainInit BlockchainInitHook + OnBlockchainTerminate BlockchainTerminateHook + OnBlockStart BlockStartHook + OnBlockEnd BlockEndHook + OnSkippedBlock SkippedBlockHook + OnGenesisBlock GenesisBlockHook // State events OnBalanceChange BalanceChangeHook OnNonceChange NonceChangeHook OnCodeChange CodeChangeHook OnStorageChange StorageChangeHook OnLog LogHook - OnTerminate TerminateHook } // BalanceChangeReason is used to indicate the reason for a balance change, useful diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index f352be940fac..6123ca88d603 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -78,14 +78,14 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { logger: logger, } return &tracing.Hooks{ - OnBlockStart: t.OnBlockStart, - OnBlockEnd: t.OnBlockEnd, - OnGenesisBlock: t.OnGenesisBlock, - OnTxStart: t.OnTxStart, - OnBalanceChange: t.OnBalanceChange, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnTerminate: t.OnTerminate, + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnGenesisBlock: t.OnGenesisBlock, + OnTxStart: t.OnTxStart, + OnBalanceChange: t.OnBalanceChange, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnBlockchainTerminate: t.OnBlockchainTerminate, }, nil } @@ -230,7 +230,7 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call) } -func (s *supply) OnTerminate() { +func (s *supply) OnBlockchainTerminate() { if err := s.logger.Close(); err != nil { log.Warn("failed to close supply tracer log file", "error", err) } From a2639b2fdef521689842c8f8b99eecc5eaf6c9d0 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 25 Apr 2024 09:39:08 +0300 Subject: [PATCH 23/28] s/OnBlockchainTerminate/OnClose --- core/blockchain.go | 4 ---- eth/tracers/live/supply.go | 18 +++++++++--------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 7a1a36918da1..b45cd92e523d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1161,10 +1161,6 @@ func (bc *BlockChain) Stop() { if err := bc.triedb.Close(); err != nil { log.Error("Failed to close trie database", "err", err) } - // Allow tracers to clean-up and release resources. - if bc.vmConfig.Tracer != nil && bc.vmConfig.Tracer.OnBlockchainTerminate != nil { - bc.vmConfig.Tracer.OnBlockchainTerminate() - } log.Info("Blockchain stopped") } diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 6123ca88d603..9ab472c8324a 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -78,14 +78,14 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { logger: logger, } return &tracing.Hooks{ - OnBlockStart: t.OnBlockStart, - OnBlockEnd: t.OnBlockEnd, - OnGenesisBlock: t.OnGenesisBlock, - OnTxStart: t.OnTxStart, - OnBalanceChange: t.OnBalanceChange, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnBlockchainTerminate: t.OnBlockchainTerminate, + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnGenesisBlock: t.OnGenesisBlock, + OnTxStart: t.OnTxStart, + OnBalanceChange: t.OnBalanceChange, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnClose: t.OnClose, }, nil } @@ -230,7 +230,7 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call) } -func (s *supply) OnBlockchainTerminate() { +func (s *supply) OnClose() { if err := s.logger.Close(); err != nil { log.Warn("failed to close supply tracer log file", "error", err) } From dec5d3b6d19a21aa72f8ac07746298b60053acc4 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 30 May 2024 14:29:05 +0300 Subject: [PATCH 24/28] supply test compares output as JSON --- eth/tracers/internal/tracetest/supply_test.go | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 9942c166c02c..fef085fffc96 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -18,13 +18,13 @@ package tracetest import ( "bufio" + "bytes" "encoding/json" "fmt" "math/big" "os" "path" "path/filepath" - "reflect" "testing" "github.com/ethereum/go-ethereum/common" @@ -92,9 +92,7 @@ func TestSupplyGenesisAlloc(t *testing.T) { actual := out[expected.Number] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) } func TestSupplyRewards(t *testing.T) { @@ -123,9 +121,7 @@ func TestSupplyRewards(t *testing.T) { actual := out[expected.Number] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) } func TestSupplyEip1559Burn(t *testing.T) { @@ -185,9 +181,7 @@ func TestSupplyEip1559Burn(t *testing.T) { ) actual := out[expected.Number] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) } func TestSupplyWithdrawals(t *testing.T) { @@ -227,9 +221,7 @@ func TestSupplyWithdrawals(t *testing.T) { actual = out[expected.Number] ) - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) } // Tests fund retrieval after contract's selfdestruct. @@ -325,9 +317,7 @@ func TestSupplySelfdestruct(t *testing.T) { actual := preCancunOutput[expected.Number] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("Pre-cancun incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) // 2. Test post Cancun cancunTime := uint64(0) @@ -368,9 +358,7 @@ func TestSupplySelfdestruct(t *testing.T) { actual = postCancunOutput[expected.Number] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("Post-cancun incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) } // Tests selfdestructing contract to send its balance to itself (burn). @@ -518,9 +506,7 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { actual := output[expected.Number] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("incorrect supply info: expected %+v, got %+v", expected, actual) - } + compareAsJSON(t, expected, actual) } func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) { @@ -575,3 +561,19 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG return output, chain, nil } + +func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { + want, err := json.Marshal(expected) + if err != nil { + t.Fatalf("failed to marshal expected value to JSON: %v", err) + } + + have, err := json.Marshal(actual) + if err != nil { + t.Fatalf("failed to marshal actual value to JSON: %v", err) + } + + if !bytes.Equal(want, have) { + t.Fatalf("incorrect supply info: expected %s, got %s", string(want), string(have)) + } +} From b07ef33e95e85b6427257c341aa855364e3c6f83 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 30 May 2024 14:33:28 +0300 Subject: [PATCH 25/28] change output JSON schema The new output becomes: ``` { "issuance": { "reward": 0, "withdrawals": 0 }, "burn": { "1559": 0, "blob": 0, "misc": 0 }, "blockNumber": 1, "hash": "0x", "parentHash": "0x" } ``` --- eth/tracers/internal/tracetest/supply_test.go | 154 +++++++++++------- eth/tracers/live/supply.go | 54 +++--- 2 files changed, 131 insertions(+), 77 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index fef085fffc96..1ef2439deb9c 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -42,11 +42,21 @@ import ( _ "github.com/ethereum/go-ethereum/eth/tracers/live" ) +type supplyInfoIssuance struct { + GenesisAlloc *big.Int `json:"genesisAlloc,omitempty"` + Reward *big.Int `json:"reward"` + Withdrawals *big.Int `json:"withdrawals"` +} + +type supplyInfoBurn struct { + EIP1559 *big.Int `json:"1559"` + Blob *big.Int `json:"blob"` + Misc *big.Int `json:"misc"` +} + type supplyInfo struct { - Delta *big.Int `json:"delta"` - Reward *big.Int `json:"reward"` - Withdrawals *big.Int `json:"withdrawals"` - Burn *big.Int `json:"burn"` + Issuance *supplyInfoIssuance `json:"issuance"` + Burn *supplyInfoBurn `json:"burn"` // Block info Number uint64 `json:"blockNumber"` @@ -76,13 +86,19 @@ func TestSupplyGenesisAlloc(t *testing.T) { ) expected := supplyInfo{ - Delta: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), - Reward: common.Big0, - Withdrawals: common.Big0, - Burn: common.Big0, - Number: 0, - Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), - ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + Issuance: &supplyInfoIssuance{ + GenesisAlloc: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), + Reward: common.Big0, + Withdrawals: common.Big0, + }, + Burn: &supplyInfoBurn{ + EIP1559: common.Big0, + Blob: common.Big0, + Misc: common.Big0, + }, + Number: 0, + Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), + ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), } out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) @@ -105,13 +121,18 @@ func TestSupplyRewards(t *testing.T) { ) expected := supplyInfo{ - Delta: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), - Reward: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), - Withdrawals: common.Big0, - Burn: common.Big0, - Number: 1, - Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), - ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), + Issuance: &supplyInfoIssuance{ + Reward: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), + Withdrawals: common.Big0, + }, + Burn: &supplyInfoBurn{ + EIP1559: common.Big0, + Blob: common.Big0, + Misc: common.Big0, + }, + Number: 1, + Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), + ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), } out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) @@ -170,13 +191,18 @@ func TestSupplyEip1559Burn(t *testing.T) { reward = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) expected = supplyInfo{ - Delta: new(big.Int).Sub(reward, burn), - Reward: reward, - Withdrawals: common.Big0, - Burn: burn, - Number: 1, - Hash: head.Hash(), - ParentHash: head.ParentHash, + Issuance: &supplyInfoIssuance{ + Reward: reward, + Withdrawals: common.Big0, + }, + Burn: &supplyInfoBurn{ + EIP1559: burn, + Blob: common.Big0, + Misc: common.Big0, + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, } ) @@ -210,13 +236,18 @@ func TestSupplyWithdrawals(t *testing.T) { var ( head = chain.CurrentBlock() expected = supplyInfo{ - Delta: big.NewInt(1337000000000), - Reward: common.Big0, - Withdrawals: big.NewInt(1337000000000), - Burn: common.Big0, - Number: 1, - Hash: head.Hash(), - ParentHash: head.ParentHash, + Issuance: &supplyInfoIssuance{ + Reward: common.Big0, + Withdrawals: big.NewInt(1337000000000), + }, + Burn: &supplyInfoBurn{ + EIP1559: common.Big0, + Blob: common.Big0, + Misc: common.Big0, + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, } actual = out[expected.Number] ) @@ -306,13 +337,18 @@ func TestSupplySelfdestruct(t *testing.T) { head := preCancunChain.CurrentBlock() // Check live trace output expected := supplyInfo{ - Delta: big.NewInt(-55294500000000), - Reward: common.Big0, - Withdrawals: common.Big0, - Burn: big.NewInt(55294500000000), - Number: 1, - Hash: head.Hash(), - ParentHash: head.ParentHash, + Issuance: &supplyInfoIssuance{ + Reward: common.Big0, + Withdrawals: common.Big0, + }, + Burn: &supplyInfoBurn{ + EIP1559: big.NewInt(55289500000000), + Blob: common.Big0, + Misc: big.NewInt(5000000000), + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, } actual := preCancunOutput[expected.Number] @@ -347,13 +383,18 @@ func TestSupplySelfdestruct(t *testing.T) { // Check live trace output head = postCancunChain.CurrentBlock() expected = supplyInfo{ - Delta: big.NewInt(-55289500000000), - Reward: common.Big0, - Withdrawals: common.Big0, - Burn: big.NewInt(55289500000000), - Number: 1, - Hash: head.Hash(), - ParentHash: head.ParentHash, + Issuance: &supplyInfoIssuance{ + Reward: common.Big0, + Withdrawals: common.Big0, + }, + Burn: &supplyInfoBurn{ + EIP1559: big.NewInt(55289500000000), + Blob: common.Big0, + Misc: common.Big0, + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, } actual = postCancunOutput[expected.Number] @@ -491,17 +532,20 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { // Check live trace output block := chain.GetBlockByNumber(1) - blockBurn := new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed()))) - totalBurn := new(big.Int).Add(blockBurn, eth5) // 5ETH burned from contract B expected := supplyInfo{ - Delta: new(big.Int).Neg(totalBurn), - Reward: common.Big0, - Withdrawals: common.Big0, - Burn: totalBurn, - Number: 1, - Hash: block.Hash(), - ParentHash: block.ParentHash(), + Issuance: &supplyInfoIssuance{ + Reward: common.Big0, + Withdrawals: common.Big0, + }, + Burn: &supplyInfoBurn{ + EIP1559: new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed()))), + Blob: common.Big0, + Misc: eth5, // 5ETH burned from contract B + }, + Number: 1, + Hash: block.Hash(), + ParentHash: block.ParentHash(), } actual := output[expected.Number] diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 9ab472c8324a..a9ffbae14c5d 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -21,11 +21,21 @@ func init() { tracers.LiveDirectory.Register("supply", newSupply) } +type supplyInfoIssuance struct { + GenesisAlloc *big.Int `json:"genesisAlloc,omitempty"` + Reward *big.Int `json:"reward"` + Withdrawals *big.Int `json:"withdrawals"` +} + +type supplyInfoBurn struct { + EIP1559 *big.Int `json:"1559"` + Blob *big.Int `json:"blob"` + Misc *big.Int `json:"misc"` +} + type supplyInfo struct { - Delta *big.Int `json:"delta"` - Reward *big.Int `json:"reward"` - Withdrawals *big.Int `json:"withdrawals"` - Burn *big.Int `json:"burn"` + Issuance *supplyInfoIssuance `json:"issuance"` + Burn *supplyInfoBurn `json:"burn"` // Block info Number uint64 `json:"blockNumber"` @@ -33,11 +43,6 @@ type supplyInfo struct { ParentHash common.Hash `json:"parentHash"` } -func (s *supplyInfo) burn(amount *big.Int) { - s.Burn.Add(s.Burn, amount) - s.Delta.Sub(s.Delta, amount) -} - type supplyTxCallstack struct { calls []supplyTxCallstack burn *big.Int @@ -91,10 +96,15 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { func newSupplyInfo() supplyInfo { return supplyInfo{ - Delta: big.NewInt(0), - Reward: big.NewInt(0), - Withdrawals: big.NewInt(0), - Burn: big.NewInt(0), + Issuance: &supplyInfoIssuance{ + Reward: big.NewInt(0), + Withdrawals: big.NewInt(0), + }, + Burn: &supplyInfoBurn{ + EIP1559: big.NewInt(0), + Blob: big.NewInt(0), + Misc: big.NewInt(0), + }, Number: 0, Hash: common.Hash{}, @@ -116,7 +126,7 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { // Calculate Burn for this block if ev.Block.BaseFee() != nil { burn := new(big.Int).Mul(new(big.Int).SetUint64(ev.Block.GasUsed()), ev.Block.BaseFee()) - s.delta.burn(burn) + s.delta.Burn.EIP1559.Add(s.delta.Burn.EIP1559, burn) } // Blob burnt gas if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil { @@ -125,7 +135,7 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { baseFee = eip4844.CalcBlobFee(excess) burn = new(big.Int).Mul(new(big.Int).SetUint64(*blobGas), baseFee) ) - s.delta.burn(burn) + s.delta.Burn.Blob.Add(s.delta.Burn.Blob, burn) } } @@ -140,9 +150,11 @@ func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.delta.Hash = b.Hash() s.delta.ParentHash = b.ParentHash() + s.delta.Issuance.GenesisAlloc = big.NewInt(0) + // Initialize supply with total allocation in genesis block for _, account := range alloc { - s.delta.Delta.Add(s.delta.Delta, account.Balance) + s.delta.Issuance.GenesisAlloc.Add(s.delta.Issuance.GenesisAlloc, account.Balance) } s.write(s.delta) @@ -155,18 +167,16 @@ func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big. switch reason { case tracing.BalanceIncreaseRewardMineUncle: case tracing.BalanceIncreaseRewardMineBlock: - s.delta.Reward.Add(s.delta.Reward, diff) + s.delta.Issuance.Reward.Add(s.delta.Issuance.Reward, diff) case tracing.BalanceIncreaseWithdrawal: - s.delta.Withdrawals.Add(s.delta.Withdrawals, diff) + s.delta.Issuance.Withdrawals.Add(s.delta.Issuance.Withdrawals, diff) case tracing.BalanceDecreaseSelfdestructBurn: // BalanceDecreaseSelfdestructBurn is non-reversible as it happens // at the end of the transaction. - s.delta.Burn.Sub(s.delta.Burn, diff) + s.delta.Burn.Misc.Sub(s.delta.Burn.Misc, diff) default: return } - - s.delta.Delta.Add(s.delta.Delta, diff) } func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { @@ -177,7 +187,7 @@ func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from co func (s *supply) internalTxsHandler(call *supplyTxCallstack) { // Handle Burned amount if call.burn != nil { - s.delta.burn(call.burn) + s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn) } if len(call.calls) > 0 { From e4a2c82af206ac600b16eebb91f09b1d6349b814 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Thu, 30 May 2024 14:42:38 +0300 Subject: [PATCH 26/28] output hex values instead of numbers --- eth/tracers/internal/tracetest/supply_test.go | 85 ++++++++++--------- eth/tracers/live/gen_supplyinfoburn.go | 49 +++++++++++ eth/tracers/live/gen_supplyinfoissuance.go | 49 +++++++++++ eth/tracers/live/supply.go | 15 ++++ 4 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 eth/tracers/live/gen_supplyinfoburn.go create mode 100644 eth/tracers/live/gen_supplyinfoissuance.go diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 1ef2439deb9c..5ecb25718d8d 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -28,6 +28,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -43,15 +44,15 @@ import ( ) type supplyInfoIssuance struct { - GenesisAlloc *big.Int `json:"genesisAlloc,omitempty"` - Reward *big.Int `json:"reward"` - Withdrawals *big.Int `json:"withdrawals"` + GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` + Reward *hexutil.Big `json:"reward"` + Withdrawals *hexutil.Big `json:"withdrawals"` } type supplyInfoBurn struct { - EIP1559 *big.Int `json:"1559"` - Blob *big.Int `json:"blob"` - Misc *big.Int `json:"misc"` + EIP1559 *hexutil.Big `json:"1559"` + Blob *hexutil.Big `json:"blob"` + Misc *hexutil.Big `json:"misc"` } type supplyInfo struct { @@ -87,14 +88,14 @@ func TestSupplyGenesisAlloc(t *testing.T) { expected := supplyInfo{ Issuance: &supplyInfoIssuance{ - GenesisAlloc: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), - Reward: common.Big0, - Withdrawals: common.Big0, + GenesisAlloc: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), + Reward: (*hexutil.Big)(common.Big0), + Withdrawals: (*hexutil.Big)(common.Big0), }, Burn: &supplyInfoBurn{ - EIP1559: common.Big0, - Blob: common.Big0, - Misc: common.Big0, + EIP1559: (*hexutil.Big)(common.Big0), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(common.Big0), }, Number: 0, Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), @@ -122,13 +123,13 @@ func TestSupplyRewards(t *testing.T) { expected := supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)), - Withdrawals: common.Big0, + Reward: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), + Withdrawals: (*hexutil.Big)(common.Big0), }, Burn: &supplyInfoBurn{ - EIP1559: common.Big0, - Blob: common.Big0, - Misc: common.Big0, + EIP1559: (*hexutil.Big)(common.Big0), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(common.Big0), }, Number: 1, Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), @@ -192,13 +193,13 @@ func TestSupplyEip1559Burn(t *testing.T) { burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) expected = supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: reward, - Withdrawals: common.Big0, + Reward: (*hexutil.Big)(reward), + Withdrawals: (*hexutil.Big)(common.Big0), }, Burn: &supplyInfoBurn{ - EIP1559: burn, - Blob: common.Big0, - Misc: common.Big0, + EIP1559: (*hexutil.Big)(burn), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(common.Big0), }, Number: 1, Hash: head.Hash(), @@ -237,13 +238,13 @@ func TestSupplyWithdrawals(t *testing.T) { head = chain.CurrentBlock() expected = supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: common.Big0, - Withdrawals: big.NewInt(1337000000000), + Reward: (*hexutil.Big)(common.Big0), + Withdrawals: (*hexutil.Big)(big.NewInt(1337000000000)), }, Burn: &supplyInfoBurn{ - EIP1559: common.Big0, - Blob: common.Big0, - Misc: common.Big0, + EIP1559: (*hexutil.Big)(common.Big0), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(common.Big0), }, Number: 1, Hash: head.Hash(), @@ -338,13 +339,13 @@ func TestSupplySelfdestruct(t *testing.T) { // Check live trace output expected := supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: common.Big0, - Withdrawals: common.Big0, + Reward: (*hexutil.Big)(common.Big0), + Withdrawals: (*hexutil.Big)(common.Big0), }, Burn: &supplyInfoBurn{ - EIP1559: big.NewInt(55289500000000), - Blob: common.Big0, - Misc: big.NewInt(5000000000), + EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(big.NewInt(5000000000)), }, Number: 1, Hash: head.Hash(), @@ -384,13 +385,13 @@ func TestSupplySelfdestruct(t *testing.T) { head = postCancunChain.CurrentBlock() expected = supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: common.Big0, - Withdrawals: common.Big0, + Reward: (*hexutil.Big)(common.Big0), + Withdrawals: (*hexutil.Big)(common.Big0), }, Burn: &supplyInfoBurn{ - EIP1559: big.NewInt(55289500000000), - Blob: common.Big0, - Misc: common.Big0, + EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(common.Big0), }, Number: 1, Hash: head.Hash(), @@ -535,13 +536,13 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { expected := supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: common.Big0, - Withdrawals: common.Big0, + Reward: (*hexutil.Big)(common.Big0), + Withdrawals: (*hexutil.Big)(common.Big0), }, Burn: &supplyInfoBurn{ - EIP1559: new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed()))), - Blob: common.Big0, - Misc: eth5, // 5ETH burned from contract B + EIP1559: (*hexutil.Big)(new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed())))), + Blob: (*hexutil.Big)(common.Big0), + Misc: (*hexutil.Big)(eth5), // 5ETH burned from contract B }, Number: 1, Hash: block.Hash(), diff --git a/eth/tracers/live/gen_supplyinfoburn.go b/eth/tracers/live/gen_supplyinfoburn.go new file mode 100644 index 000000000000..a8afe41527b5 --- /dev/null +++ b/eth/tracers/live/gen_supplyinfoburn.go @@ -0,0 +1,49 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package live + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*supplyInfoBurnMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s supplyInfoBurn) MarshalJSON() ([]byte, error) { + type supplyInfoBurn struct { + EIP1559 *hexutil.Big `json:"1559"` + Blob *hexutil.Big `json:"blob"` + Misc *hexutil.Big `json:"misc"` + } + var enc supplyInfoBurn + enc.EIP1559 = (*hexutil.Big)(s.EIP1559) + enc.Blob = (*hexutil.Big)(s.Blob) + enc.Misc = (*hexutil.Big)(s.Misc) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *supplyInfoBurn) UnmarshalJSON(input []byte) error { + type supplyInfoBurn struct { + EIP1559 *hexutil.Big `json:"1559"` + Blob *hexutil.Big `json:"blob"` + Misc *hexutil.Big `json:"misc"` + } + var dec supplyInfoBurn + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.EIP1559 != nil { + s.EIP1559 = (*big.Int)(dec.EIP1559) + } + if dec.Blob != nil { + s.Blob = (*big.Int)(dec.Blob) + } + if dec.Misc != nil { + s.Misc = (*big.Int)(dec.Misc) + } + return nil +} diff --git a/eth/tracers/live/gen_supplyinfoissuance.go b/eth/tracers/live/gen_supplyinfoissuance.go new file mode 100644 index 000000000000..384353a58ec7 --- /dev/null +++ b/eth/tracers/live/gen_supplyinfoissuance.go @@ -0,0 +1,49 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package live + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*supplyInfoIssuanceMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s supplyInfoIssuance) MarshalJSON() ([]byte, error) { + type supplyInfoIssuance struct { + GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` + Reward *hexutil.Big `json:"reward"` + Withdrawals *hexutil.Big `json:"withdrawals"` + } + var enc supplyInfoIssuance + enc.GenesisAlloc = (*hexutil.Big)(s.GenesisAlloc) + enc.Reward = (*hexutil.Big)(s.Reward) + enc.Withdrawals = (*hexutil.Big)(s.Withdrawals) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *supplyInfoIssuance) UnmarshalJSON(input []byte) error { + type supplyInfoIssuance struct { + GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` + Reward *hexutil.Big `json:"reward"` + Withdrawals *hexutil.Big `json:"withdrawals"` + } + var dec supplyInfoIssuance + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.GenesisAlloc != nil { + s.GenesisAlloc = (*big.Int)(dec.GenesisAlloc) + } + if dec.Reward != nil { + s.Reward = (*big.Int)(dec.Reward) + } + if dec.Withdrawals != nil { + s.Withdrawals = (*big.Int)(dec.Withdrawals) + } + return nil +} diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index a9ffbae14c5d..f3d9cd5e3908 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -27,12 +28,26 @@ type supplyInfoIssuance struct { Withdrawals *big.Int `json:"withdrawals"` } +//go:generate go run github.com/fjl/gencodec -type supplyInfoIssuance -field-override supplyInfoIssuanceMarshaling -out gen_supplyinfoissuance.go +type supplyInfoIssuanceMarshaling struct { + GenesisAlloc *hexutil.Big + Reward *hexutil.Big + Withdrawals *hexutil.Big +} + type supplyInfoBurn struct { EIP1559 *big.Int `json:"1559"` Blob *big.Int `json:"blob"` Misc *big.Int `json:"misc"` } +//go:generate go run github.com/fjl/gencodec -type supplyInfoBurn -field-override supplyInfoBurnMarshaling -out gen_supplyinfoburn.go +type supplyInfoBurnMarshaling struct { + EIP1559 *hexutil.Big + Blob *hexutil.Big + Misc *hexutil.Big +} + type supplyInfo struct { Issuance *supplyInfoIssuance `json:"issuance"` Burn *supplyInfoBurn `json:"burn"` From 3ed1bd816bfb7da94c8617c35b58701a023f652e Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Fri, 31 May 2024 15:00:17 +0300 Subject: [PATCH 27/28] omitempty fields from supply tracer output --- eth/tracers/internal/tracetest/supply_test.go | 56 +++---------------- eth/tracers/live/gen_supplyinfoburn.go | 12 ++-- eth/tracers/live/gen_supplyinfoissuance.go | 8 +-- eth/tracers/live/supply.go | 53 +++++++++++++----- 4 files changed, 59 insertions(+), 70 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 5ecb25718d8d..de1a9990a034 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -45,19 +45,19 @@ import ( type supplyInfoIssuance struct { GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` - Reward *hexutil.Big `json:"reward"` - Withdrawals *hexutil.Big `json:"withdrawals"` + Reward *hexutil.Big `json:"reward,omitempty"` + Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` } type supplyInfoBurn struct { - EIP1559 *hexutil.Big `json:"1559"` - Blob *hexutil.Big `json:"blob"` - Misc *hexutil.Big `json:"misc"` + EIP1559 *hexutil.Big `json:"1559,omitempty"` + Blob *hexutil.Big `json:"blob,omitempty"` + Misc *hexutil.Big `json:"misc,omitempty"` } type supplyInfo struct { - Issuance *supplyInfoIssuance `json:"issuance"` - Burn *supplyInfoBurn `json:"burn"` + Issuance *supplyInfoIssuance `json:"issuance,omitempty"` + Burn *supplyInfoBurn `json:"burn,omitempty"` // Block info Number uint64 `json:"blockNumber"` @@ -89,13 +89,6 @@ func TestSupplyGenesisAlloc(t *testing.T) { expected := supplyInfo{ Issuance: &supplyInfoIssuance{ GenesisAlloc: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), - Reward: (*hexutil.Big)(common.Big0), - Withdrawals: (*hexutil.Big)(common.Big0), - }, - Burn: &supplyInfoBurn{ - EIP1559: (*hexutil.Big)(common.Big0), - Blob: (*hexutil.Big)(common.Big0), - Misc: (*hexutil.Big)(common.Big0), }, Number: 0, Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), @@ -123,13 +116,7 @@ func TestSupplyRewards(t *testing.T) { expected := supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), - Withdrawals: (*hexutil.Big)(common.Big0), - }, - Burn: &supplyInfoBurn{ - EIP1559: (*hexutil.Big)(common.Big0), - Blob: (*hexutil.Big)(common.Big0), - Misc: (*hexutil.Big)(common.Big0), + Reward: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), }, Number: 1, Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), @@ -193,13 +180,10 @@ func TestSupplyEip1559Burn(t *testing.T) { burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) expected = supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: (*hexutil.Big)(reward), - Withdrawals: (*hexutil.Big)(common.Big0), + Reward: (*hexutil.Big)(reward), }, Burn: &supplyInfoBurn{ EIP1559: (*hexutil.Big)(burn), - Blob: (*hexutil.Big)(common.Big0), - Misc: (*hexutil.Big)(common.Big0), }, Number: 1, Hash: head.Hash(), @@ -238,14 +222,8 @@ func TestSupplyWithdrawals(t *testing.T) { head = chain.CurrentBlock() expected = supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: (*hexutil.Big)(common.Big0), Withdrawals: (*hexutil.Big)(big.NewInt(1337000000000)), }, - Burn: &supplyInfoBurn{ - EIP1559: (*hexutil.Big)(common.Big0), - Blob: (*hexutil.Big)(common.Big0), - Misc: (*hexutil.Big)(common.Big0), - }, Number: 1, Hash: head.Hash(), ParentHash: head.ParentHash, @@ -338,13 +316,8 @@ func TestSupplySelfdestruct(t *testing.T) { head := preCancunChain.CurrentBlock() // Check live trace output expected := supplyInfo{ - Issuance: &supplyInfoIssuance{ - Reward: (*hexutil.Big)(common.Big0), - Withdrawals: (*hexutil.Big)(common.Big0), - }, Burn: &supplyInfoBurn{ EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), - Blob: (*hexutil.Big)(common.Big0), Misc: (*hexutil.Big)(big.NewInt(5000000000)), }, Number: 1, @@ -384,14 +357,8 @@ func TestSupplySelfdestruct(t *testing.T) { // Check live trace output head = postCancunChain.CurrentBlock() expected = supplyInfo{ - Issuance: &supplyInfoIssuance{ - Reward: (*hexutil.Big)(common.Big0), - Withdrawals: (*hexutil.Big)(common.Big0), - }, Burn: &supplyInfoBurn{ EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), - Blob: (*hexutil.Big)(common.Big0), - Misc: (*hexutil.Big)(common.Big0), }, Number: 1, Hash: head.Hash(), @@ -535,13 +502,8 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { block := chain.GetBlockByNumber(1) expected := supplyInfo{ - Issuance: &supplyInfoIssuance{ - Reward: (*hexutil.Big)(common.Big0), - Withdrawals: (*hexutil.Big)(common.Big0), - }, Burn: &supplyInfoBurn{ EIP1559: (*hexutil.Big)(new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed())))), - Blob: (*hexutil.Big)(common.Big0), Misc: (*hexutil.Big)(eth5), // 5ETH burned from contract B }, Number: 1, diff --git a/eth/tracers/live/gen_supplyinfoburn.go b/eth/tracers/live/gen_supplyinfoburn.go index a8afe41527b5..d01eda3975da 100644 --- a/eth/tracers/live/gen_supplyinfoburn.go +++ b/eth/tracers/live/gen_supplyinfoburn.go @@ -14,9 +14,9 @@ var _ = (*supplyInfoBurnMarshaling)(nil) // MarshalJSON marshals as JSON. func (s supplyInfoBurn) MarshalJSON() ([]byte, error) { type supplyInfoBurn struct { - EIP1559 *hexutil.Big `json:"1559"` - Blob *hexutil.Big `json:"blob"` - Misc *hexutil.Big `json:"misc"` + EIP1559 *hexutil.Big `json:"1559,omitempty"` + Blob *hexutil.Big `json:"blob,omitempty"` + Misc *hexutil.Big `json:"misc,omitempty"` } var enc supplyInfoBurn enc.EIP1559 = (*hexutil.Big)(s.EIP1559) @@ -28,9 +28,9 @@ func (s supplyInfoBurn) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (s *supplyInfoBurn) UnmarshalJSON(input []byte) error { type supplyInfoBurn struct { - EIP1559 *hexutil.Big `json:"1559"` - Blob *hexutil.Big `json:"blob"` - Misc *hexutil.Big `json:"misc"` + EIP1559 *hexutil.Big `json:"1559,omitempty"` + Blob *hexutil.Big `json:"blob,omitempty"` + Misc *hexutil.Big `json:"misc,omitempty"` } var dec supplyInfoBurn if err := json.Unmarshal(input, &dec); err != nil { diff --git a/eth/tracers/live/gen_supplyinfoissuance.go b/eth/tracers/live/gen_supplyinfoissuance.go index 384353a58ec7..e2536ee3252d 100644 --- a/eth/tracers/live/gen_supplyinfoissuance.go +++ b/eth/tracers/live/gen_supplyinfoissuance.go @@ -15,8 +15,8 @@ var _ = (*supplyInfoIssuanceMarshaling)(nil) func (s supplyInfoIssuance) MarshalJSON() ([]byte, error) { type supplyInfoIssuance struct { GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` - Reward *hexutil.Big `json:"reward"` - Withdrawals *hexutil.Big `json:"withdrawals"` + Reward *hexutil.Big `json:"reward,omitempty"` + Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` } var enc supplyInfoIssuance enc.GenesisAlloc = (*hexutil.Big)(s.GenesisAlloc) @@ -29,8 +29,8 @@ func (s supplyInfoIssuance) MarshalJSON() ([]byte, error) { func (s *supplyInfoIssuance) UnmarshalJSON(input []byte) error { type supplyInfoIssuance struct { GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` - Reward *hexutil.Big `json:"reward"` - Withdrawals *hexutil.Big `json:"withdrawals"` + Reward *hexutil.Big `json:"reward,omitempty"` + Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` } var dec supplyInfoIssuance if err := json.Unmarshal(input, &dec); err != nil { diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index f3d9cd5e3908..8963f2ba9e43 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -24,8 +24,8 @@ func init() { type supplyInfoIssuance struct { GenesisAlloc *big.Int `json:"genesisAlloc,omitempty"` - Reward *big.Int `json:"reward"` - Withdrawals *big.Int `json:"withdrawals"` + Reward *big.Int `json:"reward,omitempty"` + Withdrawals *big.Int `json:"withdrawals,omitempty"` } //go:generate go run github.com/fjl/gencodec -type supplyInfoIssuance -field-override supplyInfoIssuanceMarshaling -out gen_supplyinfoissuance.go @@ -36,9 +36,9 @@ type supplyInfoIssuanceMarshaling struct { } type supplyInfoBurn struct { - EIP1559 *big.Int `json:"1559"` - Blob *big.Int `json:"blob"` - Misc *big.Int `json:"misc"` + EIP1559 *big.Int `json:"1559,omitempty"` + Blob *big.Int `json:"blob,omitempty"` + Misc *big.Int `json:"misc,omitempty"` } //go:generate go run github.com/fjl/gencodec -type supplyInfoBurn -field-override supplyInfoBurnMarshaling -out gen_supplyinfoburn.go @@ -49,8 +49,8 @@ type supplyInfoBurnMarshaling struct { } type supplyInfo struct { - Issuance *supplyInfoIssuance `json:"issuance"` - Burn *supplyInfoBurn `json:"burn"` + Issuance *supplyInfoIssuance `json:"issuance,omitempty"` + Burn *supplyInfoBurn `json:"burn,omitempty"` // Block info Number uint64 `json:"blockNumber"` @@ -116,9 +116,7 @@ func newSupplyInfo() supplyInfo { Withdrawals: big.NewInt(0), }, Burn: &supplyInfoBurn{ - EIP1559: big.NewInt(0), - Blob: big.NewInt(0), - Misc: big.NewInt(0), + Misc: big.NewInt(0), }, Number: 0, @@ -141,7 +139,9 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { // Calculate Burn for this block if ev.Block.BaseFee() != nil { burn := new(big.Int).Mul(new(big.Int).SetUint64(ev.Block.GasUsed()), ev.Block.BaseFee()) - s.delta.Burn.EIP1559.Add(s.delta.Burn.EIP1559, burn) + if burn.Sign() != 0 { + s.delta.Burn.EIP1559 = burn + } } // Blob burnt gas if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil { @@ -150,7 +150,7 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { baseFee = eip4844.CalcBlobFee(excess) burn = new(big.Int).Mul(new(big.Int).SetUint64(*blobGas), baseFee) ) - s.delta.Burn.Blob.Add(s.delta.Burn.Blob, burn) + s.delta.Burn.Blob = burn } } @@ -262,7 +262,34 @@ func (s *supply) OnClose() { } func (s *supply) write(data any) { - out, _ := json.Marshal(data) + supply, ok := data.(supplyInfo) + if !ok { + log.Warn("failed to cast supply tracer data on write to log file") + return + } + + // Remove empty fields + if supply.Issuance.Reward.Sign() == 0 { + supply.Issuance.Reward = nil + } + + if supply.Issuance.Withdrawals.Sign() == 0 { + supply.Issuance.Withdrawals = nil + } + + if supply.Issuance.GenesisAlloc == nil && supply.Issuance.Reward == nil && supply.Issuance.Withdrawals == nil { + supply.Issuance = nil + } + + if supply.Burn.Misc.Sign() == 0 { + supply.Burn.Misc = nil + } + + if supply.Burn.EIP1559 == nil && supply.Burn.Blob == nil && supply.Burn.Misc == nil { + supply.Burn = nil + } + + out, _ := json.Marshal(supply) if _, err := s.logger.Write(out); err != nil { log.Warn("failed to write to supply tracer log file", "error", err) } From 405ceae2a1cb48fb000525e3773ec9887f7c1b4c Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Fri, 31 May 2024 15:02:00 +0300 Subject: [PATCH 28/28] keep logic of removing empty fields in a single place for tech debt --- eth/tracers/internal/tracetest/supply_test.go | 27 +++++++++++++++++++ eth/tracers/live/supply.go | 27 +++++++++++++------ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index de1a9990a034..2d4f1b089006 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -67,6 +67,33 @@ type supplyInfo struct { func emptyBlockGenerationFunc(b *core.BlockGen) {} +func TestSupplyOmittedFields(t *testing.T) { + var ( + config = *params.MergedTestChainConfig + gspec = &core.Genesis{ + Config: &config, + } + ) + + gspec.Config.TerminalTotalDifficulty = big.NewInt(0) + + out, _, err := testSupplyTracer(t, gspec, func(b *core.BlockGen) { + b.SetPoS() + }) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + expected := supplyInfo{ + Number: 0, + Hash: common.HexToHash("0x52f276d96f0afaaf2c3cb358868bdc2779c4b0cb8de3e7e5302e247c0b66a703"), + ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + } + actual := out[expected.Number] + + compareAsJSON(t, expected, actual) +} + func TestSupplyGenesisAlloc(t *testing.T) { var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 8963f2ba9e43..0c9141e99d9f 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -112,11 +112,14 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { func newSupplyInfo() supplyInfo { return supplyInfo{ Issuance: &supplyInfoIssuance{ - Reward: big.NewInt(0), - Withdrawals: big.NewInt(0), + GenesisAlloc: big.NewInt(0), + Reward: big.NewInt(0), + Withdrawals: big.NewInt(0), }, Burn: &supplyInfoBurn{ - Misc: big.NewInt(0), + EIP1559: big.NewInt(0), + Blob: big.NewInt(0), + Misc: big.NewInt(0), }, Number: 0, @@ -139,9 +142,7 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { // Calculate Burn for this block if ev.Block.BaseFee() != nil { burn := new(big.Int).Mul(new(big.Int).SetUint64(ev.Block.GasUsed()), ev.Block.BaseFee()) - if burn.Sign() != 0 { - s.delta.Burn.EIP1559 = burn - } + s.delta.Burn.EIP1559 = burn } // Blob burnt gas if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil { @@ -165,8 +166,6 @@ func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.delta.Hash = b.Hash() s.delta.ParentHash = b.ParentHash() - s.delta.Issuance.GenesisAlloc = big.NewInt(0) - // Initialize supply with total allocation in genesis block for _, account := range alloc { s.delta.Issuance.GenesisAlloc.Add(s.delta.Issuance.GenesisAlloc, account.Balance) @@ -269,6 +268,10 @@ func (s *supply) write(data any) { } // Remove empty fields + if supply.Issuance.GenesisAlloc.Sign() == 0 { + supply.Issuance.GenesisAlloc = nil + } + if supply.Issuance.Reward.Sign() == 0 { supply.Issuance.Reward = nil } @@ -281,6 +284,14 @@ func (s *supply) write(data any) { supply.Issuance = nil } + if supply.Burn.EIP1559.Sign() == 0 { + supply.Burn.EIP1559 = nil + } + + if supply.Burn.Blob.Sign() == 0 { + supply.Burn.Blob = nil + } + if supply.Burn.Misc.Sign() == 0 { supply.Burn.Misc = nil }