github.com/ethereum/go-ethereum@v1.16.1/eth/tracers/live/supply.go (about)

     1  // Copyright 2024 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package live
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"path/filepath"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/common/hexutil"
    28  	"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
    29  	"github.com/ethereum/go-ethereum/core/tracing"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  	"github.com/ethereum/go-ethereum/core/vm"
    32  	"github.com/ethereum/go-ethereum/eth/tracers"
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/ethereum/go-ethereum/params"
    35  	"gopkg.in/natefinch/lumberjack.v2"
    36  )
    37  
    38  func init() {
    39  	tracers.LiveDirectory.Register("supply", newSupplyTracer)
    40  }
    41  
    42  type supplyInfoIssuance struct {
    43  	GenesisAlloc *big.Int `json:"genesisAlloc,omitempty"`
    44  	Reward       *big.Int `json:"reward,omitempty"`
    45  	Withdrawals  *big.Int `json:"withdrawals,omitempty"`
    46  }
    47  
    48  //go:generate go run github.com/fjl/gencodec -type supplyInfoIssuance -field-override supplyInfoIssuanceMarshaling -out gen_supplyinfoissuance.go
    49  type supplyInfoIssuanceMarshaling struct {
    50  	GenesisAlloc *hexutil.Big
    51  	Reward       *hexutil.Big
    52  	Withdrawals  *hexutil.Big
    53  }
    54  
    55  type supplyInfoBurn struct {
    56  	EIP1559 *big.Int `json:"1559,omitempty"`
    57  	Blob    *big.Int `json:"blob,omitempty"`
    58  	Misc    *big.Int `json:"misc,omitempty"`
    59  }
    60  
    61  //go:generate go run github.com/fjl/gencodec -type supplyInfoBurn -field-override supplyInfoBurnMarshaling -out gen_supplyinfoburn.go
    62  type supplyInfoBurnMarshaling struct {
    63  	EIP1559 *hexutil.Big
    64  	Blob    *hexutil.Big
    65  	Misc    *hexutil.Big
    66  }
    67  
    68  type supplyInfo struct {
    69  	Issuance *supplyInfoIssuance `json:"issuance,omitempty"`
    70  	Burn     *supplyInfoBurn     `json:"burn,omitempty"`
    71  
    72  	// Block info
    73  	Number     uint64      `json:"blockNumber"`
    74  	Hash       common.Hash `json:"hash"`
    75  	ParentHash common.Hash `json:"parentHash"`
    76  }
    77  
    78  type supplyTxCallstack struct {
    79  	calls []supplyTxCallstack
    80  	burn  *big.Int
    81  }
    82  
    83  type supplyTracer struct {
    84  	delta       supplyInfo
    85  	txCallstack []supplyTxCallstack // Callstack for current transaction
    86  	logger      *lumberjack.Logger
    87  	chainConfig *params.ChainConfig
    88  }
    89  
    90  type supplyTracerConfig struct {
    91  	Path    string `json:"path"`    // Path to the directory where the tracer logs will be stored
    92  	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.
    93  }
    94  
    95  func newSupplyTracer(cfg json.RawMessage) (*tracing.Hooks, error) {
    96  	var config supplyTracerConfig
    97  	if err := json.Unmarshal(cfg, &config); err != nil {
    98  		return nil, fmt.Errorf("failed to parse config: %v", err)
    99  	}
   100  	if config.Path == "" {
   101  		return nil, errors.New("supply tracer output path is required")
   102  	}
   103  
   104  	// Store traces in a rotating file
   105  	logger := &lumberjack.Logger{
   106  		Filename: filepath.Join(config.Path, "supply.jsonl"),
   107  	}
   108  	if config.MaxSize > 0 {
   109  		logger.MaxSize = config.MaxSize
   110  	}
   111  
   112  	t := &supplyTracer{
   113  		delta:  newSupplyInfo(),
   114  		logger: logger,
   115  	}
   116  	return &tracing.Hooks{
   117  		OnBlockchainInit: t.onBlockchainInit,
   118  		OnBlockStart:     t.onBlockStart,
   119  		OnBlockEnd:       t.onBlockEnd,
   120  		OnGenesisBlock:   t.onGenesisBlock,
   121  		OnTxStart:        t.onTxStart,
   122  		OnBalanceChange:  t.onBalanceChange,
   123  		OnEnter:          t.onEnter,
   124  		OnExit:           t.onExit,
   125  		OnClose:          t.onClose,
   126  	}, nil
   127  }
   128  
   129  func newSupplyInfo() supplyInfo {
   130  	return supplyInfo{
   131  		Issuance: &supplyInfoIssuance{
   132  			GenesisAlloc: big.NewInt(0),
   133  			Reward:       big.NewInt(0),
   134  			Withdrawals:  big.NewInt(0),
   135  		},
   136  		Burn: &supplyInfoBurn{
   137  			EIP1559: big.NewInt(0),
   138  			Blob:    big.NewInt(0),
   139  			Misc:    big.NewInt(0),
   140  		},
   141  
   142  		Number:     0,
   143  		Hash:       common.Hash{},
   144  		ParentHash: common.Hash{},
   145  	}
   146  }
   147  
   148  func (s *supplyTracer) resetDelta() {
   149  	s.delta = newSupplyInfo()
   150  }
   151  
   152  func (s *supplyTracer) onBlockchainInit(chainConfig *params.ChainConfig) {
   153  	s.chainConfig = chainConfig
   154  }
   155  
   156  func (s *supplyTracer) onBlockStart(ev tracing.BlockEvent) {
   157  	s.resetDelta()
   158  
   159  	s.delta.Number = ev.Block.NumberU64()
   160  	s.delta.Hash = ev.Block.Hash()
   161  	s.delta.ParentHash = ev.Block.ParentHash()
   162  
   163  	// Calculate Burn for this block
   164  	if ev.Block.BaseFee() != nil {
   165  		burn := new(big.Int).Mul(new(big.Int).SetUint64(ev.Block.GasUsed()), ev.Block.BaseFee())
   166  		s.delta.Burn.EIP1559 = burn
   167  	}
   168  	// Blob burnt gas
   169  	if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil {
   170  		var (
   171  			baseFee = eip4844.CalcBlobFee(s.chainConfig, ev.Block.Header())
   172  			burn    = new(big.Int).Mul(new(big.Int).SetUint64(*blobGas), baseFee)
   173  		)
   174  		s.delta.Burn.Blob = burn
   175  	}
   176  }
   177  
   178  func (s *supplyTracer) onBlockEnd(err error) {
   179  	s.write(s.delta)
   180  }
   181  
   182  func (s *supplyTracer) onGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
   183  	s.resetDelta()
   184  
   185  	s.delta.Number = b.NumberU64()
   186  	s.delta.Hash = b.Hash()
   187  	s.delta.ParentHash = b.ParentHash()
   188  
   189  	// Initialize supply with total allocation in genesis block
   190  	for _, account := range alloc {
   191  		s.delta.Issuance.GenesisAlloc.Add(s.delta.Issuance.GenesisAlloc, account.Balance)
   192  	}
   193  
   194  	s.write(s.delta)
   195  }
   196  
   197  func (s *supplyTracer) onBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) {
   198  	diff := new(big.Int).Sub(newBalance, prevBalance)
   199  
   200  	// NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock
   201  	switch reason {
   202  	case tracing.BalanceIncreaseRewardMineUncle:
   203  	case tracing.BalanceIncreaseRewardMineBlock:
   204  		s.delta.Issuance.Reward.Add(s.delta.Issuance.Reward, diff)
   205  	case tracing.BalanceIncreaseWithdrawal:
   206  		s.delta.Issuance.Withdrawals.Add(s.delta.Issuance.Withdrawals, diff)
   207  	case tracing.BalanceDecreaseSelfdestructBurn:
   208  		// BalanceDecreaseSelfdestructBurn is non-reversible as it happens
   209  		// at the end of the transaction.
   210  		s.delta.Burn.Misc.Sub(s.delta.Burn.Misc, diff)
   211  	default:
   212  		return
   213  	}
   214  }
   215  
   216  func (s *supplyTracer) onTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {
   217  	s.txCallstack = make([]supplyTxCallstack, 0, 1)
   218  }
   219  
   220  // internalTxsHandler handles internal transactions burned amount
   221  func (s *supplyTracer) internalTxsHandler(call *supplyTxCallstack) {
   222  	// Handle Burned amount
   223  	if call.burn != nil {
   224  		s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn)
   225  	}
   226  
   227  	// Recursively handle internal calls
   228  	for _, call := range call.calls {
   229  		callCopy := call
   230  		s.internalTxsHandler(&callCopy)
   231  	}
   232  }
   233  
   234  func (s *supplyTracer) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   235  	call := supplyTxCallstack{
   236  		calls: make([]supplyTxCallstack, 0),
   237  	}
   238  
   239  	// This is a special case of burned amount which has to be handled here
   240  	// which happens when type == selfdestruct and from == to.
   241  	if vm.OpCode(typ) == vm.SELFDESTRUCT && from == to && value.Cmp(common.Big0) == 1 {
   242  		call.burn = value
   243  	}
   244  
   245  	// Append call to the callstack, so we can fill the details in CaptureExit
   246  	s.txCallstack = append(s.txCallstack, call)
   247  }
   248  
   249  func (s *supplyTracer) onExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
   250  	if depth == 0 {
   251  		// No need to handle Burned amount if transaction is reverted
   252  		if !reverted {
   253  			s.internalTxsHandler(&s.txCallstack[0])
   254  		}
   255  		return
   256  	}
   257  
   258  	size := len(s.txCallstack)
   259  	if size <= 1 {
   260  		return
   261  	}
   262  	// Pop call
   263  	call := s.txCallstack[size-1]
   264  	s.txCallstack = s.txCallstack[:size-1]
   265  	size -= 1
   266  
   267  	// In case of a revert, we can drop the call and all its subcalls.
   268  	// Caution, that this has to happen after popping the call from the stack.
   269  	if reverted {
   270  		return
   271  	}
   272  	s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call)
   273  }
   274  
   275  func (s *supplyTracer) onClose() {
   276  	if err := s.logger.Close(); err != nil {
   277  		log.Warn("failed to close supply tracer log file", "error", err)
   278  	}
   279  }
   280  
   281  func (s *supplyTracer) write(data any) {
   282  	supply, ok := data.(supplyInfo)
   283  	if !ok {
   284  		log.Warn("failed to cast supply tracer data on write to log file")
   285  		return
   286  	}
   287  
   288  	// Remove empty fields
   289  	if supply.Issuance.GenesisAlloc.Sign() == 0 {
   290  		supply.Issuance.GenesisAlloc = nil
   291  	}
   292  
   293  	if supply.Issuance.Reward.Sign() == 0 {
   294  		supply.Issuance.Reward = nil
   295  	}
   296  
   297  	if supply.Issuance.Withdrawals.Sign() == 0 {
   298  		supply.Issuance.Withdrawals = nil
   299  	}
   300  
   301  	if supply.Issuance.GenesisAlloc == nil && supply.Issuance.Reward == nil && supply.Issuance.Withdrawals == nil {
   302  		supply.Issuance = nil
   303  	}
   304  
   305  	if supply.Burn.EIP1559.Sign() == 0 {
   306  		supply.Burn.EIP1559 = nil
   307  	}
   308  
   309  	if supply.Burn.Blob.Sign() == 0 {
   310  		supply.Burn.Blob = nil
   311  	}
   312  
   313  	if supply.Burn.Misc.Sign() == 0 {
   314  		supply.Burn.Misc = nil
   315  	}
   316  
   317  	if supply.Burn.EIP1559 == nil && supply.Burn.Blob == nil && supply.Burn.Misc == nil {
   318  		supply.Burn = nil
   319  	}
   320  
   321  	out, _ := json.Marshal(supply)
   322  	if _, err := s.logger.Write(out); err != nil {
   323  		log.Warn("failed to write to supply tracer log file", "error", err)
   324  	}
   325  	if _, err := s.logger.Write([]byte{'\n'}); err != nil {
   326  		log.Warn("failed to write to supply tracer log file", "error", err)
   327  	}
   328  }