github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/eth/dataparser.go (about)

     1  package eth
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"math/big"
     7  	"runtime/debug"
     8  	"strconv"
     9  	"strings"
    10  	"unicode"
    11  	"unicode/utf8"
    12  
    13  	"github.com/ethereum/go-ethereum/accounts/abi"
    14  	"github.com/golang/glog"
    15  	"github.com/trezor/blockbook/bchain"
    16  )
    17  
    18  func parseSimpleNumericProperty(data string) *big.Int {
    19  	if has0xPrefix(data) {
    20  		data = data[2:]
    21  	}
    22  	if len(data) > 64 {
    23  		data = data[:64]
    24  	}
    25  	if len(data) == 64 {
    26  		var n big.Int
    27  		_, ok := n.SetString(data, 16)
    28  		if ok {
    29  			return &n
    30  		}
    31  	}
    32  	return nil
    33  }
    34  
    35  func parseSimpleStringProperty(data string) string {
    36  	if has0xPrefix(data) {
    37  		data = data[2:]
    38  	}
    39  	if len(data) > 128 {
    40  		n := parseSimpleNumericProperty(data[64:128])
    41  		if n != nil {
    42  			l := n.Int64()
    43  			if l > 0 && int(l) <= ((len(data)-128)>>1) {
    44  				b, err := hex.DecodeString(data[128 : 128+2*l])
    45  				if err == nil {
    46  					return string(b)
    47  				}
    48  			}
    49  		}
    50  	}
    51  	// allow string properties as UTF-8 data
    52  	b, err := hex.DecodeString(data)
    53  	if err == nil {
    54  		i := bytes.Index(b, []byte{0})
    55  		if i > 32 {
    56  			i = 32
    57  		}
    58  		if i > 0 {
    59  			b = b[:i]
    60  		}
    61  		if utf8.Valid(b) {
    62  			return string(b)
    63  		}
    64  	}
    65  	return ""
    66  }
    67  
    68  func decamel(s string) string {
    69  	var b bytes.Buffer
    70  	splittable := false
    71  	for i, v := range s {
    72  		if i == 0 {
    73  			b.WriteRune(unicode.ToUpper(v))
    74  		} else {
    75  			if splittable && unicode.IsUpper(v) {
    76  				b.WriteByte(' ')
    77  			}
    78  			b.WriteRune(v)
    79  			// special handling of ETH to be able to convert "addETHToContract" to "Add ETH To Contract"
    80  			splittable = unicode.IsLower(v) || unicode.IsNumber(v) || (i >= 2 && s[i-2:i+1] == "ETH")
    81  		}
    82  	}
    83  	return b.String()
    84  }
    85  
    86  func GetSignatureFromData(data string) uint32 {
    87  	if has0xPrefix(data) {
    88  		data = data[2:]
    89  	}
    90  	if len(data) < 8 {
    91  		return 0
    92  	}
    93  	sig, err := strconv.ParseUint(data[:8], 16, 32)
    94  	if err != nil {
    95  		return 0
    96  	}
    97  	return uint32(sig)
    98  }
    99  
   100  const ErrorTy byte = 255
   101  
   102  func processParam(data string, index int, dataOffset int, t *abi.Type, processed []bool) ([]string, int, bool) {
   103  	var retval []string
   104  	d := index << 6
   105  	if d+64 > len(data) {
   106  		return nil, 0, false
   107  	}
   108  	block := data[d : d+64]
   109  	switch t.T {
   110  	// static types
   111  	case abi.IntTy, abi.UintTy, abi.BoolTy:
   112  		var n big.Int
   113  		_, ok := n.SetString(block, 16)
   114  		if !ok {
   115  			return nil, 0, false
   116  		}
   117  		if t.T == abi.BoolTy {
   118  			if n.Int64() != 0 {
   119  				retval = []string{"true"}
   120  			} else {
   121  				retval = []string{"false"}
   122  			}
   123  		} else {
   124  			retval = []string{n.String()}
   125  		}
   126  		processed[index] = true
   127  		index++
   128  	case abi.AddressTy:
   129  		b, err := hex.DecodeString(block[24:])
   130  		if err != nil {
   131  			return nil, 0, false
   132  		}
   133  		retval = []string{EIP55Address(b)}
   134  		processed[index] = true
   135  		index++
   136  	case abi.FixedBytesTy:
   137  		retval = []string{"0x" + block[:t.Size<<1]}
   138  		processed[index] = true
   139  		index++
   140  	case abi.ArrayTy:
   141  		for i := 0; i < t.Size; i++ {
   142  			var r []string
   143  			var ok bool
   144  			r, index, ok = processParam(data, index, dataOffset, t.Elem, processed)
   145  			if !ok {
   146  				return nil, 0, false
   147  			}
   148  			retval = append(retval, r...)
   149  		}
   150  	// dynamic types
   151  	case abi.StringTy, abi.BytesTy, abi.SliceTy:
   152  		// get offset of dynamic type
   153  		offset, err := strconv.ParseInt(block, 16, 64)
   154  		if err != nil {
   155  			return nil, 0, false
   156  		}
   157  		processed[index] = true
   158  		index++
   159  		offset <<= 1
   160  		d = int(offset) + dataOffset
   161  		dynIndex := d >> 6
   162  		if d+64 > len(data) || d < 0 {
   163  			return nil, 0, false
   164  		}
   165  		// get element count of dynamic type
   166  		c, err := strconv.ParseInt(data[d:d+64], 16, 64)
   167  		if err != nil {
   168  			return nil, 0, false
   169  		}
   170  		count := int(c)
   171  		processed[dynIndex] = true
   172  		dynIndex++
   173  		if t.T == abi.StringTy || t.T == abi.BytesTy {
   174  			d += 64
   175  			de := d + (count << 1)
   176  			if de > len(data) || de < 0 {
   177  				return nil, 0, false
   178  			}
   179  			if count == 0 {
   180  				retval = []string{""}
   181  			} else {
   182  				block = data[d:de]
   183  				if t.T == abi.StringTy {
   184  					b, err := hex.DecodeString(block)
   185  					if err != nil {
   186  						return nil, 0, false
   187  					}
   188  					retval = []string{string(b)}
   189  				} else {
   190  					retval = []string{"0x" + block}
   191  				}
   192  				count = ((count - 1) >> 5) + 1
   193  				for i := 0; i < count; i++ {
   194  					processed[dynIndex] = true
   195  					dynIndex++
   196  				}
   197  			}
   198  		} else {
   199  			newOffset := dataOffset + dynIndex<<6
   200  			for i := 0; i < count; i++ {
   201  				var r []string
   202  				var ok bool
   203  				r, dynIndex, ok = processParam(data, dynIndex, newOffset, t.Elem, processed)
   204  				if !ok {
   205  					return nil, 0, false
   206  				}
   207  				retval = append(retval, r...)
   208  			}
   209  		}
   210  	// types not processed
   211  	case abi.HashTy, abi.FixedPointTy, abi.FunctionTy, abi.TupleTy:
   212  		fallthrough
   213  	default:
   214  		return nil, 0, false
   215  	}
   216  	return retval, index, true
   217  }
   218  
   219  func tryParseParams(data string, params []string, parsedParams []abi.Type) []bchain.EthereumParsedInputParam {
   220  	processed := make([]bool, len(data)/64)
   221  	parsed := make([]bchain.EthereumParsedInputParam, len(params))
   222  	index := 0
   223  	var values []string
   224  	var ok bool
   225  	for i := range params {
   226  		t := &parsedParams[i]
   227  		values, index, ok = processParam(data, index, 0, t, processed)
   228  		if !ok {
   229  			return nil
   230  		}
   231  		parsed[i] = bchain.EthereumParsedInputParam{Type: params[i], Values: values}
   232  	}
   233  	// all data must be processed, otherwise wrong signature
   234  	for _, p := range processed {
   235  		if !p {
   236  			return nil
   237  		}
   238  	}
   239  	return parsed
   240  }
   241  
   242  // ParseInputData tries to parse transaction input data from known FourByteSignatures
   243  // as there may be multiple signatures for the same four bytes, it tries to match the input to the known parameters
   244  // it does not parse tuples for now
   245  func ParseInputData(signatures *[]bchain.FourByteSignature, data string) *bchain.EthereumParsedInputData {
   246  	if len(data) <= 2 { // data is empty or 0x
   247  		return &bchain.EthereumParsedInputData{Name: "Transfer"}
   248  	}
   249  	if len(data) < 10 {
   250  		return nil
   251  	}
   252  	parsed := bchain.EthereumParsedInputData{
   253  		MethodId: data[:10],
   254  	}
   255  	defer func() {
   256  		if r := recover(); r != nil {
   257  			glog.Error("ParseInputData recovered from panic: ", r, ", ", data, ",signatures ", signatures)
   258  			debug.PrintStack()
   259  		}
   260  	}()
   261  	if signatures != nil {
   262  		data = data[10:]
   263  		for i := range *signatures {
   264  			s := &(*signatures)[i]
   265  			// if not yet done, set DecamelName and Function and parse parameter types from string to abi.Type
   266  			// the signatures are stored in cache
   267  			if s.DecamelName == "" {
   268  				s.DecamelName = decamel(s.Name)
   269  				s.Function = s.Name + "(" + strings.Join(s.Parameters, ", ") + ")"
   270  				s.ParsedParameters = make([]abi.Type, len(s.Parameters))
   271  				for j := range s.Parameters {
   272  					var t abi.Type
   273  					if len(s.Parameters[j]) > 0 && s.Parameters[j][0] == '(' {
   274  						// Tuple type is not supported for now
   275  						t = abi.Type{T: abi.TupleTy}
   276  					} else {
   277  						var err error
   278  						t, err = abi.NewType(s.Parameters[j], "", nil)
   279  						if err != nil {
   280  							t = abi.Type{T: ErrorTy}
   281  						}
   282  					}
   283  					s.ParsedParameters[j] = t
   284  				}
   285  			}
   286  			parsedParams := tryParseParams(data, s.Parameters, s.ParsedParameters)
   287  			if parsedParams != nil {
   288  				parsed.Name = s.DecamelName
   289  				parsed.Function = s.Function
   290  				parsed.Params = parsedParams
   291  				break
   292  			}
   293  		}
   294  	}
   295  	return &parsed
   296  }
   297  
   298  // getEnsRecord processes transaction log entry and tries to parse ENS record from it
   299  func getEnsRecord(l *rpcLogWithTxHash) *bchain.AddressAliasRecord {
   300  	if len(l.Topics) == 3 && l.Topics[0] == nameRegisteredEventSignature && len(l.Data) >= 322 {
   301  		address, err := addressFromPaddedHex(l.Topics[2])
   302  		if err != nil {
   303  			return nil
   304  		}
   305  		c, err := strconv.ParseInt(l.Data[194:194+64], 16, 64)
   306  		if err != nil {
   307  			return nil
   308  		}
   309  		de := 194 + 64 + (int(c) << 1)
   310  		if de > len(l.Data) || de < 0 {
   311  			return nil
   312  		}
   313  		b, err := hex.DecodeString(l.Data[194+64 : de])
   314  		if err != nil {
   315  			return nil
   316  		}
   317  		return &bchain.AddressAliasRecord{Address: address, Name: string(b)}
   318  	}
   319  	return nil
   320  }