github.com/bchainhub/blockbook@v0.3.2/bchain/coins/eth/ethrpc.go (about)

     1  package eth
     2  
     3  import (
     4  	"blockbook/bchain"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"math/big"
     9  	"strconv"
    10  	"sync"
    11  	"time"
    12  
    13  	ethereum "github.com/ethereum/go-ethereum"
    14  	ethcommon "github.com/ethereum/go-ethereum/common"
    15  	ethtypes "github.com/ethereum/go-ethereum/core/types"
    16  	"github.com/ethereum/go-ethereum/ethclient"
    17  	"github.com/ethereum/go-ethereum/rpc"
    18  	"github.com/golang/glog"
    19  	"github.com/juju/errors"
    20  )
    21  
    22  // EthereumNet type specifies the type of ethereum network
    23  type EthereumNet uint32
    24  
    25  const (
    26  	// MainNet is production network
    27  	MainNet EthereumNet = 1
    28  	// TestNet is Ropsten test network
    29  	TestNet EthereumNet = 3
    30  )
    31  
    32  // Configuration represents json config file
    33  type Configuration struct {
    34  	CoinName                    string `json:"coin_name"`
    35  	CoinShortcut                string `json:"coin_shortcut"`
    36  	RPCURL                      string `json:"rpc_url"`
    37  	RPCTimeout                  int    `json:"rpc_timeout"`
    38  	BlockAddressesToKeep        int    `json:"block_addresses_to_keep"`
    39  	MempoolTxTimeoutHours       int    `json:"mempoolTxTimeoutHours"`
    40  	QueryBackendOnMempoolResync bool   `json:"queryBackendOnMempoolResync"`
    41  }
    42  
    43  // EthereumRPC is an interface to JSON-RPC eth service.
    44  type EthereumRPC struct {
    45  	*bchain.BaseChain
    46  	client               *ethclient.Client
    47  	rpc                  *rpc.Client
    48  	timeout              time.Duration
    49  	Parser               *EthereumParser
    50  	Mempool              *bchain.MempoolEthereumType
    51  	mempoolInitialized   bool
    52  	bestHeaderLock       sync.Mutex
    53  	bestHeader           *ethtypes.Header
    54  	bestHeaderTime       time.Time
    55  	chanNewBlock         chan *ethtypes.Header
    56  	newBlockSubscription *rpc.ClientSubscription
    57  	chanNewTx            chan ethcommon.Hash
    58  	newTxSubscription    *rpc.ClientSubscription
    59  	ChainConfig          *Configuration
    60  }
    61  
    62  // NewEthereumRPC returns new EthRPC instance.
    63  func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
    64  	var err error
    65  	var c Configuration
    66  	err = json.Unmarshal(config, &c)
    67  	if err != nil {
    68  		return nil, errors.Annotatef(err, "Invalid configuration file")
    69  	}
    70  	// keep at least 100 mappings block->addresses to allow rollback
    71  	if c.BlockAddressesToKeep < 100 {
    72  		c.BlockAddressesToKeep = 100
    73  	}
    74  
    75  	rc, ec, err := openRPC(c.RPCURL)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	s := &EthereumRPC{
    81  		BaseChain:   &bchain.BaseChain{},
    82  		client:      ec,
    83  		rpc:         rc,
    84  		ChainConfig: &c,
    85  	}
    86  
    87  	// always create parser
    88  	s.Parser = NewEthereumParser(c.BlockAddressesToKeep)
    89  	s.timeout = time.Duration(c.RPCTimeout) * time.Second
    90  
    91  	// new blocks notifications handling
    92  	// the subscription is done in Initialize
    93  	s.chanNewBlock = make(chan *ethtypes.Header)
    94  	go func() {
    95  		for {
    96  			h, ok := <-s.chanNewBlock
    97  			if !ok {
    98  				break
    99  			}
   100  			glog.V(2).Info("rpc: new block header ", h.Number)
   101  			// update best header to the new header
   102  			s.bestHeaderLock.Lock()
   103  			s.bestHeader = h
   104  			s.bestHeaderTime = time.Now()
   105  			s.bestHeaderLock.Unlock()
   106  			// notify blockbook
   107  			pushHandler(bchain.NotificationNewBlock)
   108  		}
   109  	}()
   110  
   111  	// new mempool transaction notifications handling
   112  	// the subscription is done in Initialize
   113  	s.chanNewTx = make(chan ethcommon.Hash)
   114  	go func() {
   115  		for {
   116  			t, ok := <-s.chanNewTx
   117  			if !ok {
   118  				break
   119  			}
   120  			hex := t.Hex()
   121  			if glog.V(2) {
   122  				glog.Info("rpc: new tx ", hex)
   123  			}
   124  			s.Mempool.AddTransactionToMempool(hex)
   125  			pushHandler(bchain.NotificationNewTx)
   126  		}
   127  	}()
   128  
   129  	return s, nil
   130  }
   131  
   132  func openRPC(url string) (*rpc.Client, *ethclient.Client, error) {
   133  	rc, err := rpc.Dial(url)
   134  	if err != nil {
   135  		return nil, nil, err
   136  	}
   137  	ec := ethclient.NewClient(rc)
   138  	return rc, ec, nil
   139  }
   140  
   141  // Initialize initializes ethereum rpc interface
   142  func (b *EthereumRPC) Initialize() error {
   143  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   144  	defer cancel()
   145  
   146  	id, err := b.client.NetworkID(ctx)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	// parameters for getInfo request
   152  	switch EthereumNet(id.Uint64()) {
   153  	case MainNet:
   154  		b.Testnet = false
   155  		b.Network = "livenet"
   156  		break
   157  	case TestNet:
   158  		b.Testnet = true
   159  		b.Network = "testnet"
   160  		break
   161  	default:
   162  		return errors.Errorf("Unknown network id %v", id)
   163  	}
   164  	glog.Info("rpc: block chain ", b.Network)
   165  
   166  	return nil
   167  }
   168  
   169  // CreateMempool creates mempool if not already created, however does not initialize it
   170  func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
   171  	if b.Mempool == nil {
   172  		b.Mempool = bchain.NewMempoolEthereumType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync)
   173  		glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync)
   174  	}
   175  	return b.Mempool, nil
   176  }
   177  
   178  // InitializeMempool creates subscriptions to newHeads and newPendingTransactions
   179  func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
   180  	if b.Mempool == nil {
   181  		return errors.New("Mempool not created")
   182  	}
   183  
   184  	// get initial mempool transactions
   185  	txs, err := b.GetMempoolTransactions()
   186  	if err != nil {
   187  		return err
   188  	}
   189  	for _, txid := range txs {
   190  		b.Mempool.AddTransactionToMempool(txid)
   191  	}
   192  
   193  	b.Mempool.OnNewTxAddr = onNewTxAddr
   194  
   195  	if err = b.subscribeEvents(); err != nil {
   196  		return err
   197  	}
   198  
   199  	b.mempoolInitialized = true
   200  
   201  	return nil
   202  }
   203  
   204  func (b *EthereumRPC) subscribeEvents() error {
   205  	// subscriptions
   206  	if err := b.subscribe(func() (*rpc.ClientSubscription, error) {
   207  		// invalidate the previous subscription - it is either the first one or there was an error
   208  		b.newBlockSubscription = nil
   209  		ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   210  		defer cancel()
   211  		sub, err := b.rpc.EthSubscribe(ctx, b.chanNewBlock, "newHeads")
   212  		if err != nil {
   213  			return nil, errors.Annotatef(err, "EthSubscribe newHeads")
   214  		}
   215  		b.newBlockSubscription = sub
   216  		glog.Info("Subscribed to newHeads")
   217  		return sub, nil
   218  	}); err != nil {
   219  		return err
   220  	}
   221  
   222  	if err := b.subscribe(func() (*rpc.ClientSubscription, error) {
   223  		// invalidate the previous subscription - it is either the first one or there was an error
   224  		b.newTxSubscription = nil
   225  		ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   226  		defer cancel()
   227  		sub, err := b.rpc.EthSubscribe(ctx, b.chanNewTx, "newPendingTransactions")
   228  		if err != nil {
   229  			return nil, errors.Annotatef(err, "EthSubscribe newPendingTransactions")
   230  		}
   231  		b.newTxSubscription = sub
   232  		glog.Info("Subscribed to newPendingTransactions")
   233  		return sub, nil
   234  	}); err != nil {
   235  		return err
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  // subscribe subscribes notification and tries to resubscribe in case of error
   242  func (b *EthereumRPC) subscribe(f func() (*rpc.ClientSubscription, error)) error {
   243  	s, err := f()
   244  	if err != nil {
   245  		return err
   246  	}
   247  	go func() {
   248  	Loop:
   249  		for {
   250  			// wait for error in subscription
   251  			e := <-s.Err()
   252  			// nil error means sub.Unsubscribe called, exit goroutine
   253  			if e == nil {
   254  				return
   255  			}
   256  			glog.Error("Subscription error ", e)
   257  			timer := time.NewTimer(time.Second * 2)
   258  			// try in 2 second interval to resubscribe
   259  			for {
   260  				select {
   261  				case e = <-s.Err():
   262  					if e == nil {
   263  						return
   264  					}
   265  				case <-timer.C:
   266  					ns, err := f()
   267  					if err == nil {
   268  						// subscription successful, restart wait for next error
   269  						s = ns
   270  						continue Loop
   271  					}
   272  					glog.Error("Resubscribe error ", err)
   273  					timer.Reset(time.Second * 2)
   274  				}
   275  			}
   276  		}
   277  	}()
   278  	return nil
   279  }
   280  
   281  func (b *EthereumRPC) closeRPC() {
   282  	if b.newBlockSubscription != nil {
   283  		b.newBlockSubscription.Unsubscribe()
   284  	}
   285  	if b.newTxSubscription != nil {
   286  		b.newTxSubscription.Unsubscribe()
   287  	}
   288  	if b.rpc != nil {
   289  		b.rpc.Close()
   290  	}
   291  }
   292  
   293  func (b *EthereumRPC) reconnectRPC() error {
   294  	glog.Info("Reconnecting RPC")
   295  	b.closeRPC()
   296  	rc, ec, err := openRPC(b.ChainConfig.RPCURL)
   297  	if err != nil {
   298  		return err
   299  	}
   300  	b.rpc = rc
   301  	b.client = ec
   302  	return b.subscribeEvents()
   303  }
   304  
   305  // Shutdown cleans up rpc interface to ethereum
   306  func (b *EthereumRPC) Shutdown(ctx context.Context) error {
   307  	b.closeRPC()
   308  	close(b.chanNewBlock)
   309  	glog.Info("rpc: shutdown")
   310  	return nil
   311  }
   312  
   313  // GetCoinName returns coin name
   314  func (b *EthereumRPC) GetCoinName() string {
   315  	return b.ChainConfig.CoinName
   316  }
   317  
   318  // GetSubversion returns empty string, ethereum does not have subversion
   319  func (b *EthereumRPC) GetSubversion() string {
   320  	return ""
   321  }
   322  
   323  // GetChainInfo returns information about the connected backend
   324  func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
   325  	h, err := b.getBestHeader()
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   330  	defer cancel()
   331  	id, err := b.client.NetworkID(ctx)
   332  	if err != nil {
   333  		return nil, err
   334  	}
   335  	var ver, protocol string
   336  	if err := b.rpc.CallContext(ctx, &ver, "web3_clientVersion"); err != nil {
   337  		return nil, err
   338  	}
   339  	if err := b.rpc.CallContext(ctx, &protocol, "eth_protocolVersion"); err != nil {
   340  		return nil, err
   341  	}
   342  	rv := &bchain.ChainInfo{
   343  		Blocks:          int(h.Number.Int64()),
   344  		Bestblockhash:   h.Hash().Hex(),
   345  		Difficulty:      h.Difficulty.String(),
   346  		Version:         ver,
   347  		ProtocolVersion: protocol,
   348  	}
   349  	idi := int(id.Uint64())
   350  	if idi == 1 {
   351  		rv.Chain = "mainnet"
   352  	} else {
   353  		rv.Chain = "testnet " + strconv.Itoa(idi)
   354  	}
   355  	return rv, nil
   356  }
   357  
   358  func (b *EthereumRPC) getBestHeader() (*ethtypes.Header, error) {
   359  	b.bestHeaderLock.Lock()
   360  	defer b.bestHeaderLock.Unlock()
   361  	// if the best header was not updated for 15 minutes, there could be a subscription problem, reconnect RPC
   362  	// do it only in case of normal operation, not initial synchronization
   363  	if b.bestHeaderTime.Add(15*time.Minute).Before(time.Now()) && !b.bestHeaderTime.IsZero() && b.mempoolInitialized {
   364  		err := b.reconnectRPC()
   365  		if err != nil {
   366  			return nil, err
   367  		}
   368  		b.bestHeader = nil
   369  	}
   370  	if b.bestHeader == nil {
   371  		var err error
   372  		ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   373  		defer cancel()
   374  		b.bestHeader, err = b.client.HeaderByNumber(ctx, nil)
   375  		if err != nil {
   376  			b.bestHeader = nil
   377  			return nil, err
   378  		}
   379  		b.bestHeaderTime = time.Now()
   380  	}
   381  	return b.bestHeader, nil
   382  }
   383  
   384  // GetBestBlockHash returns hash of the tip of the best-block-chain
   385  func (b *EthereumRPC) GetBestBlockHash() (string, error) {
   386  	h, err := b.getBestHeader()
   387  	if err != nil {
   388  		return "", err
   389  	}
   390  	return h.Hash().Hex(), nil
   391  }
   392  
   393  // GetBestBlockHeight returns height of the tip of the best-block-chain
   394  func (b *EthereumRPC) GetBestBlockHeight() (uint32, error) {
   395  	h, err := b.getBestHeader()
   396  	if err != nil {
   397  		return 0, err
   398  	}
   399  	return uint32(h.Number.Uint64()), nil
   400  }
   401  
   402  // GetBlockHash returns hash of block in best-block-chain at given height
   403  func (b *EthereumRPC) GetBlockHash(height uint32) (string, error) {
   404  	var n big.Int
   405  	n.SetUint64(uint64(height))
   406  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   407  	defer cancel()
   408  	h, err := b.client.HeaderByNumber(ctx, &n)
   409  	if err != nil {
   410  		if err == ethereum.NotFound {
   411  			return "", bchain.ErrBlockNotFound
   412  		}
   413  		return "", errors.Annotatef(err, "height %v", height)
   414  	}
   415  	return h.Hash().Hex(), nil
   416  }
   417  
   418  func (b *EthereumRPC) ethHeaderToBlockHeader(h *rpcHeader) (*bchain.BlockHeader, error) {
   419  	height, err := ethNumber(h.Number)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	c, err := b.computeConfirmations(uint64(height))
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  	time, err := ethNumber(h.Time)
   428  	if err != nil {
   429  		return nil, err
   430  	}
   431  	size, err := ethNumber(h.Size)
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  	return &bchain.BlockHeader{
   436  		Hash:          h.Hash,
   437  		Prev:          h.ParentHash,
   438  		Height:        uint32(height),
   439  		Confirmations: int(c),
   440  		Time:          time,
   441  		Size:          int(size),
   442  	}, nil
   443  }
   444  
   445  // GetBlockHeader returns header of block with given hash
   446  func (b *EthereumRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
   447  	raw, err := b.getBlockRaw(hash, 0, false)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  	var h rpcHeader
   452  	if err := json.Unmarshal(raw, &h); err != nil {
   453  		return nil, errors.Annotatef(err, "hash %v", hash)
   454  	}
   455  	return b.ethHeaderToBlockHeader(&h)
   456  }
   457  
   458  func (b *EthereumRPC) computeConfirmations(n uint64) (uint32, error) {
   459  	bh, err := b.getBestHeader()
   460  	if err != nil {
   461  		return 0, err
   462  	}
   463  	bn := bh.Number.Uint64()
   464  	// transaction in the best block has 1 confirmation
   465  	return uint32(bn - n + 1), nil
   466  }
   467  
   468  func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (json.RawMessage, error) {
   469  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   470  	defer cancel()
   471  	var raw json.RawMessage
   472  	var err error
   473  	if hash != "" {
   474  		if hash == "pending" {
   475  			err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", hash, fullTxs)
   476  		} else {
   477  			err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByHash", ethcommon.HexToHash(hash), fullTxs)
   478  		}
   479  	} else {
   480  		err = b.rpc.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), fullTxs)
   481  	}
   482  	if err != nil {
   483  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   484  	} else if len(raw) == 0 {
   485  		return nil, bchain.ErrBlockNotFound
   486  	}
   487  	return raw, nil
   488  }
   489  
   490  func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*rpcLog, error) {
   491  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   492  	defer cancel()
   493  	var logs []rpcLogWithTxHash
   494  	err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{
   495  		"fromBlock": blockNumber,
   496  		"toBlock":   blockNumber,
   497  		"topics":    []string{erc20TransferEventSignature},
   498  	})
   499  	if err != nil {
   500  		return nil, errors.Annotatef(err, "blockNumber %v", blockNumber)
   501  	}
   502  	r := make(map[string][]*rpcLog)
   503  	for i := range logs {
   504  		l := &logs[i]
   505  		r[l.Hash] = append(r[l.Hash], &l.rpcLog)
   506  	}
   507  	return r, nil
   508  }
   509  
   510  // GetBlock returns block with given hash or height, hash has precedence if both passed
   511  func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
   512  	raw, err := b.getBlockRaw(hash, height, true)
   513  	if err != nil {
   514  		return nil, err
   515  	}
   516  	var head rpcHeader
   517  	if err := json.Unmarshal(raw, &head); err != nil {
   518  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   519  	}
   520  	var body rpcBlockTransactions
   521  	if err := json.Unmarshal(raw, &body); err != nil {
   522  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   523  	}
   524  	bbh, err := b.ethHeaderToBlockHeader(&head)
   525  	if err != nil {
   526  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   527  	}
   528  	// get ERC20 events
   529  	logs, err := b.getERC20EventsForBlock(head.Number)
   530  	if err != nil {
   531  		return nil, err
   532  	}
   533  	btxs := make([]bchain.Tx, len(body.Transactions))
   534  	for i := range body.Transactions {
   535  		tx := &body.Transactions[i]
   536  		btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, bbh.Time, uint32(bbh.Confirmations), true)
   537  		if err != nil {
   538  			return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash)
   539  		}
   540  		btxs[i] = *btx
   541  		if b.mempoolInitialized {
   542  			b.Mempool.RemoveTransactionFromMempool(tx.Hash)
   543  		}
   544  	}
   545  	bbk := bchain.Block{
   546  		BlockHeader: *bbh,
   547  		Txs:         btxs,
   548  	}
   549  	return &bbk, nil
   550  }
   551  
   552  // GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
   553  func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
   554  	raw, err := b.getBlockRaw(hash, 0, false)
   555  	if err != nil {
   556  		return nil, err
   557  	}
   558  	var head rpcHeader
   559  	var txs rpcBlockTxids
   560  	if err := json.Unmarshal(raw, &head); err != nil {
   561  		return nil, errors.Annotatef(err, "hash %v", hash)
   562  	}
   563  	if err = json.Unmarshal(raw, &txs); err != nil {
   564  		return nil, err
   565  	}
   566  	bch, err := b.ethHeaderToBlockHeader(&head)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	return &bchain.BlockInfo{
   571  		BlockHeader: *bch,
   572  		Difficulty:  json.Number(head.Difficulty),
   573  		Nonce:       json.Number(head.Nonce),
   574  		Txids:       txs.Transactions,
   575  	}, nil
   576  }
   577  
   578  // GetTransactionForMempool returns a transaction by the transaction ID.
   579  // It could be optimized for mempool, i.e. without block time and confirmations
   580  func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
   581  	return b.GetTransaction(txid)
   582  }
   583  
   584  // GetTransaction returns a transaction by the transaction ID.
   585  func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
   586  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   587  	defer cancel()
   588  	var tx *rpcTransaction
   589  	hash := ethcommon.HexToHash(txid)
   590  	err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash)
   591  	if err != nil {
   592  		return nil, err
   593  	} else if tx == nil {
   594  		if b.mempoolInitialized {
   595  			b.Mempool.RemoveTransactionFromMempool(txid)
   596  		}
   597  		return nil, bchain.ErrTxNotFound
   598  	}
   599  	var btx *bchain.Tx
   600  	if tx.BlockNumber == "" {
   601  		// mempool tx
   602  		btx, err = b.Parser.ethTxToTx(tx, nil, 0, 0, true)
   603  		if err != nil {
   604  			return nil, errors.Annotatef(err, "txid %v", txid)
   605  		}
   606  	} else {
   607  		// non mempool tx - read the block header to get the block time
   608  		raw, err := b.getBlockRaw(tx.BlockHash, 0, false)
   609  		if err != nil {
   610  			return nil, err
   611  		}
   612  		var ht struct {
   613  			Time string `json:"timestamp"`
   614  		}
   615  		if err := json.Unmarshal(raw, &ht); err != nil {
   616  			return nil, errors.Annotatef(err, "hash %v", hash)
   617  		}
   618  		var time int64
   619  		if time, err = ethNumber(ht.Time); err != nil {
   620  			return nil, errors.Annotatef(err, "txid %v", txid)
   621  		}
   622  		var receipt rpcReceipt
   623  		err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash)
   624  		if err != nil {
   625  			return nil, errors.Annotatef(err, "txid %v", txid)
   626  		}
   627  		n, err := ethNumber(tx.BlockNumber)
   628  		if err != nil {
   629  			return nil, errors.Annotatef(err, "txid %v", txid)
   630  		}
   631  		confirmations, err := b.computeConfirmations(uint64(n))
   632  		if err != nil {
   633  			return nil, errors.Annotatef(err, "txid %v", txid)
   634  		}
   635  		btx, err = b.Parser.ethTxToTx(tx, &receipt, time, confirmations, true)
   636  		if err != nil {
   637  			return nil, errors.Annotatef(err, "txid %v", txid)
   638  		}
   639  		// remove tx from mempool if it is there
   640  		if b.mempoolInitialized {
   641  			b.Mempool.RemoveTransactionFromMempool(txid)
   642  		}
   643  	}
   644  	return btx, nil
   645  }
   646  
   647  // GetTransactionSpecific returns json as returned by backend, with all coin specific data
   648  func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
   649  	csd, ok := tx.CoinSpecificData.(completeTransaction)
   650  	if !ok {
   651  		ntx, err := b.GetTransaction(tx.Txid)
   652  		if err != nil {
   653  			return nil, err
   654  		}
   655  		csd, ok = ntx.CoinSpecificData.(completeTransaction)
   656  		if !ok {
   657  			return nil, errors.New("Cannot get CoinSpecificData")
   658  		}
   659  	}
   660  	m, err := json.Marshal(&csd)
   661  	return json.RawMessage(m), err
   662  }
   663  
   664  // GetMempoolTransactions returns transactions in mempool
   665  func (b *EthereumRPC) GetMempoolTransactions() ([]string, error) {
   666  	raw, err := b.getBlockRaw("pending", 0, false)
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  	var body rpcBlockTxids
   671  	if len(raw) > 0 {
   672  		if err := json.Unmarshal(raw, &body); err != nil {
   673  			return nil, err
   674  		}
   675  	}
   676  	return body.Transactions, nil
   677  }
   678  
   679  // EstimateFee returns fee estimation
   680  func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) {
   681  	return b.EstimateSmartFee(blocks, true)
   682  }
   683  
   684  // EstimateSmartFee returns fee estimation
   685  func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
   686  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   687  	defer cancel()
   688  	var r big.Int
   689  	gp, err := b.client.SuggestGasPrice(ctx)
   690  	if err == nil && b != nil {
   691  		r = *gp
   692  	}
   693  	return r, err
   694  }
   695  
   696  func getStringFromMap(p string, params map[string]interface{}) (string, bool) {
   697  	v, ok := params[p]
   698  	if ok {
   699  		s, ok := v.(string)
   700  		return s, ok
   701  	}
   702  	return "", false
   703  }
   704  
   705  // EthereumTypeEstimateGas returns estimation of gas consumption for given transaction parameters
   706  func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) {
   707  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   708  	defer cancel()
   709  	msg := ethereum.CallMsg{}
   710  	s, ok := getStringFromMap("from", params)
   711  	if ok && len(s) > 0 {
   712  		msg.From = ethcommon.HexToAddress(s)
   713  	}
   714  	s, ok = getStringFromMap("to", params)
   715  	if ok && len(s) > 0 {
   716  		a := ethcommon.HexToAddress(s)
   717  		msg.To = &a
   718  	}
   719  	s, ok = getStringFromMap("data", params)
   720  	if ok && len(s) > 0 {
   721  		msg.Data = ethcommon.FromHex(s)
   722  	}
   723  	return b.client.EstimateGas(ctx, msg)
   724  }
   725  
   726  // SendRawTransaction sends raw transaction
   727  func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) {
   728  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   729  	defer cancel()
   730  	var raw json.RawMessage
   731  	err := b.rpc.CallContext(ctx, &raw, "eth_sendRawTransaction", hex)
   732  	if err != nil {
   733  		return "", err
   734  	} else if len(raw) == 0 {
   735  		return "", errors.New("SendRawTransaction: failed")
   736  	}
   737  	var result string
   738  	if err := json.Unmarshal(raw, &result); err != nil {
   739  		return "", errors.Annotatef(err, "raw result %v", raw)
   740  	}
   741  	if result == "" {
   742  		return "", errors.New("SendRawTransaction: failed, empty result")
   743  	}
   744  	return result, nil
   745  }
   746  
   747  // EthereumTypeGetBalance returns current balance of an address
   748  func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) {
   749  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   750  	defer cancel()
   751  	return b.client.BalanceAt(ctx, ethcommon.BytesToAddress(addrDesc), nil)
   752  }
   753  
   754  // EthereumTypeGetNonce returns current balance of an address
   755  func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) {
   756  	ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
   757  	defer cancel()
   758  	return b.client.NonceAt(ctx, ethcommon.BytesToAddress(addrDesc), nil)
   759  }
   760  
   761  // GetChainParser returns ethereum BlockChainParser
   762  func (b *EthereumRPC) GetChainParser() bchain.BlockChainParser {
   763  	return b.Parser
   764  }