github.com/ethereum/go-ethereum@v1.16.1/internal/ethapi/logtracer.go (about) 1 // Copyright 2023 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 ethapi 18 19 import ( 20 "math/big" 21 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/core/tracing" 24 "github.com/ethereum/go-ethereum/core/types" 25 "github.com/ethereum/go-ethereum/core/vm" 26 ) 27 28 var ( 29 // keccak256("Transfer(address,address,uint256)") 30 transferTopic = common.HexToHash("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") 31 // ERC-7528 32 transferAddress = common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") 33 ) 34 35 // tracer is a simple tracer that records all logs and 36 // ether transfers. Transfers are recorded as if they 37 // were logs. Transfer events include: 38 // - tx value 39 // - call value 40 // - self destructs 41 // 42 // The log format for a transfer is: 43 // - address: 0x0000000000000000000000000000000000000000 44 // - data: Value 45 // - topics: 46 // - Transfer(address,address,uint256) 47 // - Sender address 48 // - Recipient address 49 type tracer struct { 50 // logs keeps logs for all open call frames. 51 // This lets us clear logs for failed calls. 52 logs [][]*types.Log 53 count int 54 traceTransfers bool 55 blockNumber uint64 56 blockHash common.Hash 57 txHash common.Hash 58 txIdx uint 59 } 60 61 func newTracer(traceTransfers bool, blockNumber uint64, blockHash, txHash common.Hash, txIndex uint) *tracer { 62 return &tracer{ 63 traceTransfers: traceTransfers, 64 blockNumber: blockNumber, 65 blockHash: blockHash, 66 txHash: txHash, 67 txIdx: txIndex, 68 } 69 } 70 71 func (t *tracer) Hooks() *tracing.Hooks { 72 return &tracing.Hooks{ 73 OnEnter: t.onEnter, 74 OnExit: t.onExit, 75 OnLog: t.onLog, 76 } 77 } 78 79 func (t *tracer) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 80 t.logs = append(t.logs, make([]*types.Log, 0)) 81 if vm.OpCode(typ) != vm.DELEGATECALL && value != nil && value.Cmp(common.Big0) > 0 { 82 t.captureTransfer(from, to, value) 83 } 84 } 85 86 func (t *tracer) onExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { 87 if depth == 0 { 88 t.onEnd(reverted) 89 return 90 } 91 size := len(t.logs) 92 if size <= 1 { 93 return 94 } 95 // pop call 96 call := t.logs[size-1] 97 t.logs = t.logs[:size-1] 98 size-- 99 100 // Clear logs if call failed. 101 if !reverted { 102 t.logs[size-1] = append(t.logs[size-1], call...) 103 } 104 } 105 106 func (t *tracer) onEnd(reverted bool) { 107 if reverted { 108 t.logs[0] = nil 109 } 110 } 111 112 func (t *tracer) onLog(log *types.Log) { 113 t.captureLog(log.Address, log.Topics, log.Data) 114 } 115 116 func (t *tracer) captureLog(address common.Address, topics []common.Hash, data []byte) { 117 t.logs[len(t.logs)-1] = append(t.logs[len(t.logs)-1], &types.Log{ 118 Address: address, 119 Topics: topics, 120 Data: data, 121 BlockNumber: t.blockNumber, 122 BlockHash: t.blockHash, 123 TxHash: t.txHash, 124 TxIndex: t.txIdx, 125 Index: uint(t.count), 126 }) 127 t.count++ 128 } 129 130 func (t *tracer) captureTransfer(from, to common.Address, value *big.Int) { 131 if !t.traceTransfers { 132 return 133 } 134 topics := []common.Hash{ 135 transferTopic, 136 common.BytesToHash(from.Bytes()), 137 common.BytesToHash(to.Bytes()), 138 } 139 t.captureLog(transferAddress, topics, common.BigToHash(value).Bytes()) 140 } 141 142 // reset prepares the tracer for the next transaction. 143 func (t *tracer) reset(txHash common.Hash, txIdx uint) { 144 t.logs = nil 145 t.txHash = txHash 146 t.txIdx = txIdx 147 } 148 149 func (t *tracer) Logs() []*types.Log { 150 return t.logs[0] 151 }