github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/uniq/uniq_appearances.go (about) 1 package uniq 2 3 import ( 4 "fmt" 5 "strings" 6 "sync" 7 8 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 9 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 10 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 11 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 12 ) 13 14 // AddMiner adds the miner address (for use with post-merge) 15 func AddMiner(chain string, miner base.Address, bn base.Blknum, addrMap AddressBooleanMap) (err error) { 16 addAddressToMaps(miner.Hex(), bn, types.BlockReward, addrMap) 17 return nil 18 } 19 20 // UniqFromWithdrawals extracts addresses from an array of receipts 21 func UniqFromWithdrawals(chain string, withdrawals []types.Withdrawal, bn base.Blknum, addrMap AddressBooleanMap) (err error) { 22 for _, withdrawal := range withdrawals { 23 addAddressToMaps(withdrawal.Address.Hex(), bn, types.WithdrawalAmt, addrMap) 24 } 25 return nil 26 } 27 28 // UniqFromReceipts extracts addresses from an array of receipts 29 func UniqFromReceipts(chain string, receipts []types.Receipt, addrMap AddressBooleanMap) (err error) { 30 for _, receipt := range receipts { 31 created := receipt.ContractAddress 32 addAddressToMaps(created.Hex(), receipt.BlockNumber, receipt.TransactionIndex, addrMap) 33 if err := uniqFromLogs(chain, receipt.Logs, addrMap); err != nil { 34 return err 35 } 36 } 37 return nil 38 } 39 40 // uniqFromLogs extracts addresses from the logs 41 func uniqFromLogs(chain string, logs []types.Log, addrMap AddressBooleanMap) (err error) { 42 for _, log := range logs { 43 for _, topic := range log.Topics { 44 str := string(topic.Hex()[2:]) 45 if IsImplicitAddress(str) { 46 addAddressToMaps(str, log.BlockNumber, log.TransactionIndex, addrMap) 47 } 48 } 49 50 if len(log.Data) > 2 { 51 inputData := log.Data[2:] 52 for i := 0; i < len(inputData)/64; i++ { 53 str := string(inputData[i*64 : (i+1)*64]) 54 if IsImplicitAddress(str) { 55 addAddressToMaps(str, log.BlockNumber, log.TransactionIndex, addrMap) 56 } 57 } 58 } 59 } 60 61 return 62 } 63 64 // UniqFromTraces extracts addresses from traces 65 func UniqFromTraces(chain string, traces []types.Trace, addrMap AddressBooleanMap) (err error) { 66 conn := rpc.TempConnection(chain) 67 68 for _, trace := range traces { 69 bn := base.Blknum(trace.BlockNumber) 70 txid := trace.TransactionIndex 71 72 from := trace.Action.From.Hex() 73 addAddressToMaps(from, bn, txid, addrMap) 74 75 to := trace.Action.To.Hex() 76 addAddressToMaps(to, bn, txid, addrMap) 77 78 if trace.TraceType == "call" { 79 // If it's a call, get the to and from, we're done 80 81 } else if trace.TraceType == "reward" { 82 if trace.Action.RewardType == "block" { 83 author := trace.Action.Author.Hex() 84 fakeId := types.BlockReward 85 if base.IsPrecompile(author) { 86 author = base.SentinalAddr.Hex() 87 fakeId = types.MisconfigReward 88 } 89 addAddressToMaps(author, bn, fakeId, addrMap) 90 91 } else if trace.Action.RewardType == "uncle" { 92 author := trace.Action.Author.Hex() 93 fakeId := types.UncleReward 94 if base.IsPrecompile(author) { 95 author = base.SentinalAddr.Hex() 96 fakeId = types.MisconfigReward 97 } 98 addAddressToMaps(author, bn, fakeId, addrMap) 99 100 } else if trace.Action.RewardType == "external" { 101 // This only happens in xDai as far as we know... 102 author := trace.Action.Author.Hex() 103 addAddressToMaps(author, bn, types.ExternalReward, addrMap) 104 105 } else { 106 logger.Warn(fmt.Sprintf("Unknown reward type %s for trace: %d.%d.%d", trace.Action.RewardType, trace.BlockNumber, trace.TransactionIndex, trace.TraceIndex)) 107 return err 108 } 109 110 } else if trace.TraceType == "suicide" { 111 // add the contract that died, and where it sent it's money 112 refundAddress := trace.Action.RefundAddress.Hex() 113 addAddressToMaps(refundAddress, bn, txid, addrMap) 114 115 address := trace.Action.Address.Hex() 116 addAddressToMaps(address, bn, txid, addrMap) 117 118 } else if trace.TraceType == "create" { 119 if trace.Result != nil { 120 // may be both...record the self-destruct instead of the creation since we can only report on one 121 address := trace.Result.Address.Hex() 122 addAddressToMaps(address, bn, txid, addrMap) 123 } 124 125 // If it's a top level trace, then the call data is the init, 126 // so to match with TrueBlocks, we just parse init 127 if len(trace.TraceAddress) == 0 { 128 if len(trace.Action.Init) > 10 { 129 initData := trace.Action.Init[10:] 130 for i := 0; i < len(initData)/64; i++ { 131 str := string(initData[i*64 : (i+1)*64]) 132 if IsImplicitAddress(str) { 133 addAddressToMaps(str, bn, txid, addrMap) 134 } 135 } 136 } 137 } 138 139 // Handle contract creations that may have errored out 140 if trace.Action.To.IsZero() { 141 if trace.Result != nil && trace.Result.Address.IsZero() { 142 if trace.Error != "" { 143 if receipt, err := conn.GetReceiptNoTimestamp(bn, txid); err == nil { 144 address := receipt.ContractAddress.Hex() 145 addAddressToMaps(address, bn, txid, addrMap) 146 } 147 } 148 } 149 } 150 151 } else { 152 if len(trace.TraceType) > 0 && trace.BlockNumber != 0 { 153 logger.Warn(fmt.Sprintf("Unknown trace type %s for trace: %d.%d.%d", trace.TraceType, trace.BlockNumber, trace.TransactionIndex, trace.TraceIndex)) 154 } 155 return err 156 } 157 158 // Try to get addresses from the input data 159 if len(trace.Action.Input) > 10 { 160 inputData := trace.Action.Input[10:] 161 for i := 0; i < len(inputData)/64; i++ { 162 str := string(inputData[i*64 : (i+1)*64]) 163 if IsImplicitAddress(str) { 164 addAddressToMaps(str, bn, txid, addrMap) 165 } 166 } 167 } 168 169 // Parse output of trace 170 if trace.Result != nil && len(trace.Result.Output) > 2 { 171 outputData := trace.Result.Output[2:] 172 for i := 0; i < len(outputData)/64; i++ { 173 str := string(outputData[i*64 : (i+1)*64]) 174 if IsImplicitAddress(str) { 175 addAddressToMaps(str, bn, txid, addrMap) 176 } 177 } 178 } 179 } 180 181 return 182 } 183 184 var mapSync sync.Mutex 185 186 // addAddressToMaps helps keep track of appearances for an address. An appearance is inserted into `appsMap` 187 // if we've never seen this appearance before. `appsMap` is used to build the appearance table when writing the 188 // chunk. `addrMap` helps eliminate duplicates and is used to build the address table when writing the chunk. 189 // Precompiles are ignored. If the given address string does not start with a lead `0x`, it is normalized. 190 func addAddressToMaps(address string, bn base.Blknum, txid base.Txnum, addrMap AddressBooleanMap) { 191 if base.IsPrecompile(address) { 192 return 193 } 194 195 // Normalize implicit strings. (Implicit strings come in 32-bytes long with no leading `0x`.) 196 if !strings.HasPrefix(address, "0x") { 197 addr := base.HexToAddress("0x" + address) 198 address = addr.Hex() 199 } 200 201 mapSync.Lock() 202 defer mapSync.Unlock() 203 204 addrMap.Insert(address, bn, txid) 205 }