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 }