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  }