github.com/cerberus-wallet/blockbook@v0.3.2/bchain/coins/eth/ethparser.go (about)

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