github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/eth/ethparser.go (about) 1 package eth 2 3 import ( 4 "encoding/hex" 5 "math/big" 6 "strconv" 7 "strings" 8 9 "github.com/ethereum/go-ethereum/common/hexutil" 10 "github.com/golang/protobuf/proto" 11 "github.com/juju/errors" 12 "github.com/trezor/blockbook/bchain" 13 "golang.org/x/crypto/sha3" 14 ) 15 16 // EthereumTypeAddressDescriptorLen - the AddressDescriptor of EthereumType has fixed length 17 const EthereumTypeAddressDescriptorLen = 20 18 19 // EthereumTypeTxidLen - the length of Txid 20 const EthereumTypeTxidLen = 32 21 22 // EtherAmountDecimalPoint defines number of decimal points in Ether amounts 23 const EtherAmountDecimalPoint = 18 24 25 // EthereumParser handle 26 type EthereumParser struct { 27 *bchain.BaseParser 28 } 29 30 // NewEthereumParser returns new EthereumParser instance 31 func NewEthereumParser(b int, addressAliases bool) *EthereumParser { 32 return &EthereumParser{&bchain.BaseParser{ 33 BlockAddressesToKeep: b, 34 AmountDecimalPoint: EtherAmountDecimalPoint, 35 AddressAliases: addressAliases, 36 }} 37 } 38 39 type rpcHeader struct { 40 Hash string `json:"hash"` 41 ParentHash string `json:"parentHash"` 42 Difficulty string `json:"difficulty"` 43 Number string `json:"number"` 44 Time string `json:"timestamp"` 45 Size string `json:"size"` 46 Nonce string `json:"nonce"` 47 } 48 49 type rpcLogWithTxHash struct { 50 bchain.RpcLog 51 Hash string `json:"transactionHash"` 52 } 53 54 type rpcBlockTransactions struct { 55 Transactions []bchain.RpcTransaction `json:"transactions"` 56 } 57 58 type rpcBlockTxids struct { 59 Transactions []string `json:"transactions"` 60 } 61 62 func ethNumber(n string) (int64, error) { 63 if len(n) > 2 { 64 return strconv.ParseInt(n[2:], 16, 64) 65 } 66 return 0, errors.Errorf("Not a number: '%v'", n) 67 } 68 69 func (p *EthereumParser) ethTxToTx(tx *bchain.RpcTransaction, receipt *bchain.RpcReceipt, internalData *bchain.EthereumInternalData, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { 70 txid := tx.Hash 71 var ( 72 fa, ta []string 73 err error 74 ) 75 if len(tx.From) > 2 { 76 if fixEIP55 { 77 tx.From = EIP55AddressFromAddress(tx.From) 78 } 79 fa = []string{tx.From} 80 } 81 if len(tx.To) > 2 { 82 if fixEIP55 { 83 tx.To = EIP55AddressFromAddress(tx.To) 84 } 85 ta = []string{tx.To} 86 } 87 if fixEIP55 && receipt != nil && receipt.Logs != nil { 88 for _, l := range receipt.Logs { 89 if len(l.Address) > 2 { 90 l.Address = EIP55AddressFromAddress(l.Address) 91 } 92 } 93 } 94 if internalData != nil { 95 // ignore empty internal data 96 if internalData.Type == bchain.CALL && len(internalData.Transfers) == 0 && len(internalData.Error) == 0 { 97 internalData = nil 98 } else { 99 if fixEIP55 { 100 for i := range internalData.Transfers { 101 it := &internalData.Transfers[i] 102 it.From = EIP55AddressFromAddress(it.From) 103 it.To = EIP55AddressFromAddress(it.To) 104 } 105 } 106 } 107 } 108 ct := bchain.EthereumSpecificData{ 109 Tx: tx, 110 InternalData: internalData, 111 Receipt: receipt, 112 } 113 vs, err := hexutil.DecodeBig(tx.Value) 114 if err != nil { 115 return nil, err 116 } 117 return &bchain.Tx{ 118 Blocktime: blocktime, 119 Confirmations: confirmations, 120 // Hex 121 // LockTime 122 Time: blocktime, 123 Txid: txid, 124 Vin: []bchain.Vin{ 125 { 126 Addresses: fa, 127 // Coinbase 128 // ScriptSig 129 // Sequence 130 // Txid 131 // Vout 132 }, 133 }, 134 Vout: []bchain.Vout{ 135 { 136 N: 0, // there is always up to one To address 137 ValueSat: *vs, 138 ScriptPubKey: bchain.ScriptPubKey{ 139 // Hex 140 Addresses: ta, 141 }, 142 }, 143 }, 144 CoinSpecificData: ct, 145 }, nil 146 } 147 148 // GetAddrDescFromVout returns internal address representation of given transaction output 149 func (p *EthereumParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { 150 if len(output.ScriptPubKey.Addresses) != 1 { 151 return nil, bchain.ErrAddressMissing 152 } 153 return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0]) 154 } 155 156 func has0xPrefix(s string) bool { 157 return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x' 158 } 159 160 // GetAddrDescFromAddress returns internal address representation of given address 161 func (p *EthereumParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 162 // github.com/ethereum/go-ethereum/common.HexToAddress does not handle address errors, using own decoding 163 if has0xPrefix(address) { 164 address = address[2:] 165 } 166 if len(address) != EthereumTypeAddressDescriptorLen*2 { 167 return nil, bchain.ErrAddressMissing 168 } 169 return hex.DecodeString(address) 170 } 171 172 // EIP55Address returns an EIP55-compliant hex string representation of the address 173 func EIP55Address(addrDesc bchain.AddressDescriptor) string { 174 raw := hexutil.Encode(addrDesc) 175 if len(raw) != 42 { 176 return raw 177 } 178 sha := sha3.NewLegacyKeccak256() 179 result := []byte(raw) 180 sha.Write(result[2:]) 181 hash := sha.Sum(nil) 182 183 for i := 2; i < len(result); i++ { 184 hashByte := hash[(i-2)>>1] 185 if i%2 == 0 { 186 hashByte = hashByte >> 4 187 } else { 188 hashByte &= 0xf 189 } 190 if result[i] > '9' && hashByte > 7 { 191 result[i] -= 32 192 } 193 } 194 return string(result) 195 } 196 197 // EIP55AddressFromAddress returns an EIP55-compliant hex string representation of the address 198 func EIP55AddressFromAddress(address string) string { 199 if has0xPrefix(address) { 200 address = address[2:] 201 } 202 b, err := hex.DecodeString(address) 203 if err != nil { 204 return address 205 } 206 return EIP55Address(b) 207 } 208 209 // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable 210 func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { 211 return []string{EIP55Address(addrDesc)}, true, nil 212 } 213 214 // GetScriptFromAddrDesc returns output script for given address descriptor 215 func (p *EthereumParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) { 216 return addrDesc, nil 217 } 218 219 func hexDecode(s string) ([]byte, error) { 220 b, err := hexutil.Decode(s) 221 if err != nil && err != hexutil.ErrEmptyString { 222 return nil, err 223 } 224 return b, nil 225 } 226 227 func hexDecodeBig(s string) ([]byte, error) { 228 b, err := hexutil.DecodeBig(s) 229 if err != nil { 230 return nil, err 231 } 232 return b.Bytes(), nil 233 } 234 235 func hexEncodeBig(b []byte) string { 236 var i big.Int 237 i.SetBytes(b) 238 return hexutil.EncodeBig(&i) 239 } 240 241 // PackTx packs transaction to byte array 242 // completeTransaction.InternalData are not packed, they are stored in a different table 243 func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 244 var err error 245 var n uint64 246 r, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) 247 if !ok { 248 return nil, errors.New("Missing CoinSpecificData") 249 } 250 pt := &ProtoCompleteTransaction{} 251 pt.Tx = &ProtoCompleteTransaction_TxType{} 252 if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil { 253 return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce) 254 } 255 // pt.BlockNumber = height 256 if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil { 257 return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber) 258 } 259 pt.BlockNumber = uint32(n) 260 pt.BlockTime = uint64(blockTime) 261 if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil { 262 return nil, errors.Annotatef(err, "From %v", r.Tx.From) 263 } 264 if pt.Tx.GasLimit, err = hexutil.DecodeUint64(r.Tx.GasLimit); err != nil { 265 return nil, errors.Annotatef(err, "GasLimit %v", r.Tx.GasLimit) 266 } 267 if pt.Tx.Hash, err = hexDecode(r.Tx.Hash); err != nil { 268 return nil, errors.Annotatef(err, "Hash %v", r.Tx.Hash) 269 } 270 if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil { 271 return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload) 272 } 273 if pt.Tx.GasPrice, err = hexDecodeBig(r.Tx.GasPrice); err != nil { 274 return nil, errors.Annotatef(err, "Price %v", r.Tx.GasPrice) 275 } 276 // if pt.R, err = hexDecodeBig(r.R); err != nil { 277 // return nil, errors.Annotatef(err, "R %v", r.R) 278 // } 279 // if pt.S, err = hexDecodeBig(r.S); err != nil { 280 // return nil, errors.Annotatef(err, "S %v", r.S) 281 // } 282 // if pt.V, err = hexDecodeBig(r.V); err != nil { 283 // return nil, errors.Annotatef(err, "V %v", r.V) 284 // } 285 if pt.Tx.To, err = hexDecode(r.Tx.To); err != nil { 286 return nil, errors.Annotatef(err, "To %v", r.Tx.To) 287 } 288 if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil { 289 return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex) 290 } 291 pt.Tx.TransactionIndex = uint32(n) 292 if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil { 293 return nil, errors.Annotatef(err, "Value %v", r.Tx.Value) 294 } 295 if r.Receipt != nil { 296 pt.Receipt = &ProtoCompleteTransaction_ReceiptType{} 297 if pt.Receipt.GasUsed, err = hexDecodeBig(r.Receipt.GasUsed); err != nil { 298 return nil, errors.Annotatef(err, "GasUsed %v", r.Receipt.GasUsed) 299 } 300 if r.Receipt.Status != "" { 301 if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil { 302 return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status) 303 } 304 } else { 305 // unknown status, use 'U' as status bytes 306 // there is a potential for conflict with value 0x55 but this is not used by any chain at this moment 307 pt.Receipt.Status = []byte{'U'} 308 } 309 ptLogs := make([]*ProtoCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs)) 310 for i, l := range r.Receipt.Logs { 311 a, err := hexutil.Decode(l.Address) 312 if err != nil { 313 return nil, errors.Annotatef(err, "Address cannot be decoded %v", l) 314 } 315 d, err := hexutil.Decode(l.Data) 316 if err != nil { 317 return nil, errors.Annotatef(err, "Data cannot be decoded %v", l) 318 } 319 t := make([][]byte, len(l.Topics)) 320 for j, s := range l.Topics { 321 t[j], err = hexutil.Decode(s) 322 if err != nil { 323 return nil, errors.Annotatef(err, "Topic cannot be decoded %v", l) 324 } 325 } 326 ptLogs[i] = &ProtoCompleteTransaction_ReceiptType_LogType{ 327 Address: a, 328 Data: d, 329 Topics: t, 330 } 331 332 } 333 pt.Receipt.Log = ptLogs 334 } 335 return proto.Marshal(pt) 336 } 337 338 // UnpackTx unpacks transaction from byte array 339 func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 340 var pt ProtoCompleteTransaction 341 err := proto.Unmarshal(buf, &pt) 342 if err != nil { 343 return nil, 0, err 344 } 345 rt := bchain.RpcTransaction{ 346 AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), 347 BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), 348 From: EIP55Address(pt.Tx.From), 349 GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit), 350 Hash: hexutil.Encode(pt.Tx.Hash), 351 Payload: hexutil.Encode(pt.Tx.Payload), 352 GasPrice: hexEncodeBig(pt.Tx.GasPrice), 353 // R: hexEncodeBig(pt.R), 354 // S: hexEncodeBig(pt.S), 355 // V: hexEncodeBig(pt.V), 356 To: EIP55Address(pt.Tx.To), 357 TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), 358 Value: hexEncodeBig(pt.Tx.Value), 359 } 360 var rr *bchain.RpcReceipt 361 if pt.Receipt != nil { 362 logs := make([]*bchain.RpcLog, len(pt.Receipt.Log)) 363 for i, l := range pt.Receipt.Log { 364 topics := make([]string, len(l.Topics)) 365 for j, t := range l.Topics { 366 topics[j] = hexutil.Encode(t) 367 } 368 logs[i] = &bchain.RpcLog{ 369 Address: EIP55Address(l.Address), 370 Data: hexutil.Encode(l.Data), 371 Topics: topics, 372 } 373 } 374 status := "" 375 // handle a special value []byte{'U'} as unknown state 376 if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' { 377 status = hexEncodeBig(pt.Receipt.Status) 378 } 379 rr = &bchain.RpcReceipt{ 380 GasUsed: hexEncodeBig(pt.Receipt.GasUsed), 381 Status: status, 382 Logs: logs, 383 } 384 } 385 // TODO handle internal transactions 386 tx, err := p.ethTxToTx(&rt, rr, nil, int64(pt.BlockTime), 0, false) 387 if err != nil { 388 return nil, 0, err 389 } 390 return tx, pt.BlockNumber, nil 391 } 392 393 // PackedTxidLen returns length in bytes of packed txid 394 func (p *EthereumParser) PackedTxidLen() int { 395 return EthereumTypeTxidLen 396 } 397 398 // PackTxid packs txid to byte array 399 func (p *EthereumParser) PackTxid(txid string) ([]byte, error) { 400 if has0xPrefix(txid) { 401 txid = txid[2:] 402 } 403 return hex.DecodeString(txid) 404 } 405 406 // UnpackTxid unpacks byte array to txid 407 func (p *EthereumParser) UnpackTxid(buf []byte) (string, error) { 408 return hexutil.Encode(buf), nil 409 } 410 411 // PackBlockHash packs block hash to byte array 412 func (p *EthereumParser) PackBlockHash(hash string) ([]byte, error) { 413 if has0xPrefix(hash) { 414 hash = hash[2:] 415 } 416 return hex.DecodeString(hash) 417 } 418 419 // UnpackBlockHash unpacks byte array to block hash 420 func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) { 421 return hexutil.Encode(buf), nil 422 } 423 424 // GetChainType returns EthereumType 425 func (p *EthereumParser) GetChainType() bchain.ChainType { 426 return bchain.ChainEthereumType 427 } 428 429 // GetHeightFromTx returns ethereum specific data from bchain.Tx 430 func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { 431 var bn string 432 csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) 433 if !ok { 434 return 0, errors.New("Missing CoinSpecificData") 435 } 436 bn = csd.Tx.BlockNumber 437 n, err := hexutil.DecodeUint64(bn) 438 if err != nil { 439 return 0, errors.Annotatef(err, "BlockNumber %v", bn) 440 } 441 return uint32(n), nil 442 } 443 444 // EthereumTypeGetTokenTransfersFromTx returns contract transfers from bchain.Tx 445 func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bchain.TokenTransfers, error) { 446 var r bchain.TokenTransfers 447 var err error 448 csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) 449 if ok { 450 if csd.Receipt != nil { 451 r, err = contractGetTransfersFromLog(csd.Receipt.Logs) 452 } else { 453 r, err = contractGetTransfersFromTx(csd.Tx) 454 } 455 if err != nil { 456 return nil, err 457 } 458 } 459 return r, nil 460 } 461 462 // FormatAddressAlias adds .eth to a name alias 463 func (p *EthereumParser) FormatAddressAlias(address string, name string) string { 464 return name + ".eth" 465 } 466 467 // TxStatus is status of transaction 468 type TxStatus int 469 470 // statuses of transaction 471 const ( 472 TxStatusUnknown = TxStatus(iota - 2) 473 TxStatusPending 474 TxStatusFailure 475 TxStatusOK 476 ) 477 478 // EthereumTxData contains ethereum specific transaction data 479 type EthereumTxData struct { 480 Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown 481 Nonce uint64 `json:"nonce"` 482 GasLimit *big.Int `json:"gaslimit"` 483 GasUsed *big.Int `json:"gasused"` 484 GasPrice *big.Int `json:"gasprice"` 485 Data string `json:"data"` 486 } 487 488 // GetEthereumTxData returns EthereumTxData from bchain.Tx 489 func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData { 490 return GetEthereumTxDataFromSpecificData(tx.CoinSpecificData) 491 } 492 493 // GetEthereumTxDataFromSpecificData returns EthereumTxData from coinSpecificData 494 func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTxData { 495 etd := EthereumTxData{Status: TxStatusPending} 496 csd, ok := coinSpecificData.(bchain.EthereumSpecificData) 497 if ok { 498 if csd.Tx != nil { 499 etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) 500 etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit) 501 etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice) 502 etd.Data = csd.Tx.Payload 503 } 504 if csd.Receipt != nil { 505 switch csd.Receipt.Status { 506 case "0x1": 507 etd.Status = TxStatusOK 508 case "": // old transactions did not set status 509 etd.Status = TxStatusUnknown 510 default: 511 etd.Status = TxStatusFailure 512 } 513 etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed) 514 } 515 } 516 return &etd 517 } 518 519 const errorOutputSignature = "08c379a0" 520 521 // ParseErrorFromOutput takes output field from internal transaction data and extracts an error message from it 522 // the output must have errorOutputSignature to be parsed 523 func ParseErrorFromOutput(output string) string { 524 if has0xPrefix(output) { 525 output = output[2:] 526 } 527 if len(output) < 8+64+64+64 || output[:8] != errorOutputSignature { 528 return "" 529 } 530 return parseSimpleStringProperty(output[8:]) 531 } 532 533 // PackInternalTransactionError packs common error messages to single byte to save DB space 534 func PackInternalTransactionError(e string) string { 535 if e == "execution reverted" { 536 return "\x01" 537 } 538 if e == "out of gas" { 539 return "\x02" 540 } 541 if e == "contract creation code storage out of gas" { 542 return "\x03" 543 } 544 if e == "max code size exceeded" { 545 return "\x04" 546 } 547 548 return e 549 } 550 551 // UnpackInternalTransactionError unpacks common error messages packed by PackInternalTransactionError 552 func UnpackInternalTransactionError(data []byte) string { 553 e := string(data) 554 e = strings.ReplaceAll(e, "\x01", "Reverted. ") 555 e = strings.ReplaceAll(e, "\x02", "Out of gas. ") 556 e = strings.ReplaceAll(e, "\x03", "Contract creation code storage out of gas. ") 557 e = strings.ReplaceAll(e, "\x04", "Max code size exceeded. ") 558 return strings.TrimSpace(e) 559 }