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  }