github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/bchain/coins/eth/ethrpc.go (about)

     1  package eth
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/big"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/cryptohub-digital/blockbook-fork/bchain"
    16  	"github.com/cryptohub-digital/blockbook-fork/common"
    17  	"github.com/ethereum/go-ethereum"
    18  	ethcommon "github.com/ethereum/go-ethereum/common"
    19  	"github.com/ethereum/go-ethereum/common/hexutil"
    20  	"github.com/ethereum/go-ethereum/core/types"
    21  	"github.com/ethereum/go-ethereum/ethclient"
    22  	"github.com/ethereum/go-ethereum/rpc"
    23  	"github.com/golang/glog"
    24  	"github.com/juju/errors"
    25  )
    26  
    27  // Network type specifies the type of ethereum network
    28  type Network uint32
    29  
    30  const (
    31  	// MainNet is production network
    32  	MainNet Network = 1
    33  	// TestNet is Ropsten test network
    34  	TestNet Network = 3
    35  	// TestNetGoerli is Goerli test network
    36  	TestNetGoerli Network = 5
    37  	// TestNetSepolia is Sepolia test network
    38  	TestNetSepolia Network = 11155111
    39  )
    40  
    41  // Configuration represents json config file
    42  type Configuration struct {
    43  	CoinName                        string `json:"coin_name"`
    44  	CoinShortcut                    string `json:"coin_shortcut"`
    45  	RPCURL                          string `json:"rpc_url"`
    46  	RPCTimeout                      int    `json:"rpc_timeout"`
    47  	BlockAddressesToKeep            int    `json:"block_addresses_to_keep"`
    48  	AddressAliases                  bool   `json:"address_aliases,omitempty"`
    49  	MempoolTxTimeoutHours           int    `json:"mempoolTxTimeoutHours"`
    50  	QueryBackendOnMempoolResync     bool   `json:"queryBackendOnMempoolResync"`
    51  	ProcessInternalTransactions     bool   `json:"processInternalTransactions"`
    52  	ProcessZeroInternalTransactions bool   `json:"processZeroInternalTransactions"`
    53  	ConsensusNodeVersionURL         string `json:"consensusNodeVersion"`
    54  }
    55  
    56  // EthereumRPC is an interface to JSON-RPC eth service.
    57  type EthereumRPC struct {
    58  	*bchain.BaseChain
    59  	Client               bchain.EVMClient
    60  	RPC                  bchain.EVMRPCClient
    61  	MainNetChainID       Network
    62  	Timeout              time.Duration
    63  	Parser               *EthereumParser
    64  	PushHandler          func(bchain.NotificationType)
    65  	OpenRPC              func(string) (bchain.EVMRPCClient, bchain.EVMClient, error)
    66  	Mempool              *bchain.MempoolEthereumType
    67  	mempoolInitialized   bool
    68  	bestHeaderLock       sync.Mutex
    69  	bestHeader           bchain.EVMHeader
    70  	bestHeaderTime       time.Time
    71  	NewBlock             bchain.EVMNewBlockSubscriber
    72  	newBlockSubscription bchain.EVMClientSubscription
    73  	NewTx                bchain.EVMNewTxSubscriber
    74  	newTxSubscription    bchain.EVMClientSubscription
    75  	ChainConfig          *Configuration
    76  }
    77  
    78  // ProcessInternalTransactions specifies if internal transactions are processed
    79  var ProcessInternalTransactions bool
    80  
    81  // NewEthereumRPC returns new EthRPC instance.
    82  func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
    83  	var err error
    84  	var c Configuration
    85  	err = json.Unmarshal(config, &c)
    86  	if err != nil {
    87  		return nil, errors.Annotatef(err, "Invalid configuration file")
    88  	}
    89  	// keep at least 100 mappings block->addresses to allow rollback
    90  	if c.BlockAddressesToKeep < 100 {
    91  		c.BlockAddressesToKeep = 100
    92  	}
    93  
    94  	s := &EthereumRPC{
    95  		BaseChain:   &bchain.BaseChain{},
    96  		ChainConfig: &c,
    97  	}
    98  
    99  	ProcessInternalTransactions = c.ProcessInternalTransactions
   100  
   101  	// always create parser
   102  	s.Parser = NewEthereumParser(c.BlockAddressesToKeep, c.AddressAliases)
   103  	s.Timeout = time.Duration(c.RPCTimeout) * time.Second
   104  	s.PushHandler = pushHandler
   105  
   106  	return s, nil
   107  }
   108  
   109  // Initialize initializes ethereum rpc interface
   110  func (b *EthereumRPC) Initialize() error {
   111  	b.OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) {
   112  		r, err := rpc.Dial(url)
   113  		if err != nil {
   114  			return nil, nil, err
   115  		}
   116  		rc := &EthereumRPCClient{Client: r}
   117  		ec := &EthereumClient{Client: ethclient.NewClient(r)}
   118  		return rc, ec, nil
   119  	}
   120  
   121  	rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	// set chain specific
   127  	b.Client = ec
   128  	b.RPC = rc
   129  	b.MainNetChainID = MainNet
   130  	b.NewBlock = &EthereumNewBlock{channel: make(chan *types.Header)}
   131  	b.NewTx = &EthereumNewTx{channel: make(chan ethcommon.Hash)}
   132  
   133  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   134  	defer cancel()
   135  
   136  	id, err := b.Client.NetworkID(ctx)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	// parameters for getInfo request
   142  	switch Network(id.Uint64()) {
   143  	case MainNet:
   144  		b.Testnet = false
   145  		b.Network = "livenet"
   146  	case TestNet:
   147  		b.Testnet = true
   148  		b.Network = "testnet"
   149  	case TestNetGoerli:
   150  		b.Testnet = true
   151  		b.Network = "goerli"
   152  	case TestNetSepolia:
   153  		b.Testnet = true
   154  		b.Network = "sepolia"
   155  	default:
   156  		return errors.Errorf("Unknown network id %v", id)
   157  	}
   158  	glog.Info("rpc: block chain ", b.Network)
   159  
   160  	return nil
   161  }
   162  
   163  // CreateMempool creates mempool if not already created, however does not initialize it
   164  func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
   165  	if b.Mempool == nil {
   166  		b.Mempool = bchain.NewMempoolEthereumType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync)
   167  		glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync)
   168  	}
   169  	return b.Mempool, nil
   170  }
   171  
   172  // InitializeMempool creates subscriptions to newHeads and newPendingTransactions
   173  func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error {
   174  	if b.Mempool == nil {
   175  		return errors.New("Mempool not created")
   176  	}
   177  
   178  	// get initial mempool transactions
   179  	txs, err := b.GetMempoolTransactions()
   180  	if err != nil {
   181  		return err
   182  	}
   183  	for _, txid := range txs {
   184  		b.Mempool.AddTransactionToMempool(txid)
   185  	}
   186  
   187  	b.Mempool.OnNewTxAddr = onNewTxAddr
   188  	b.Mempool.OnNewTx = onNewTx
   189  
   190  	if err = b.subscribeEvents(); err != nil {
   191  		return err
   192  	}
   193  
   194  	b.mempoolInitialized = true
   195  
   196  	return nil
   197  }
   198  
   199  func (b *EthereumRPC) subscribeEvents() error {
   200  	// new block notifications handling
   201  	go func() {
   202  		for {
   203  			h, ok := b.NewBlock.Read()
   204  			if !ok {
   205  				break
   206  			}
   207  			b.UpdateBestHeader(h)
   208  			// notify blockbook
   209  			b.PushHandler(bchain.NotificationNewBlock)
   210  		}
   211  	}()
   212  
   213  	// new block subscription
   214  	if err := b.subscribe(func() (bchain.EVMClientSubscription, error) {
   215  		// invalidate the previous subscription - it is either the first one or there was an error
   216  		b.newBlockSubscription = nil
   217  		ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   218  		defer cancel()
   219  		sub, err := b.RPC.EthSubscribe(ctx, b.NewBlock.Channel(), "newHeads")
   220  		if err != nil {
   221  			return nil, errors.Annotatef(err, "EthSubscribe newHeads")
   222  		}
   223  		b.newBlockSubscription = sub
   224  		glog.Info("Subscribed to newHeads")
   225  		return sub, nil
   226  	}); err != nil {
   227  		return err
   228  	}
   229  
   230  	// new mempool transaction notifications handling
   231  	go func() {
   232  		for {
   233  			t, ok := b.NewTx.Read()
   234  			if !ok {
   235  				break
   236  			}
   237  			hex := t.Hex()
   238  			if glog.V(2) {
   239  				glog.Info("rpc: new tx ", hex)
   240  			}
   241  			b.Mempool.AddTransactionToMempool(hex)
   242  			b.PushHandler(bchain.NotificationNewTx)
   243  		}
   244  	}()
   245  
   246  	// new mempool transaction subscription
   247  	if err := b.subscribe(func() (bchain.EVMClientSubscription, error) {
   248  		// invalidate the previous subscription - it is either the first one or there was an error
   249  		b.newTxSubscription = nil
   250  		ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   251  		defer cancel()
   252  		sub, err := b.RPC.EthSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions")
   253  		if err != nil {
   254  			return nil, errors.Annotatef(err, "EthSubscribe newPendingTransactions")
   255  		}
   256  		b.newTxSubscription = sub
   257  		glog.Info("Subscribed to newPendingTransactions")
   258  		return sub, nil
   259  	}); err != nil {
   260  		return err
   261  	}
   262  
   263  	return nil
   264  }
   265  
   266  // subscribe subscribes notification and tries to resubscribe in case of error
   267  func (b *EthereumRPC) subscribe(f func() (bchain.EVMClientSubscription, error)) error {
   268  	s, err := f()
   269  	if err != nil {
   270  		return err
   271  	}
   272  	go func() {
   273  	Loop:
   274  		for {
   275  			// wait for error in subscription
   276  			e := <-s.Err()
   277  			// nil error means sub.Unsubscribe called, exit goroutine
   278  			if e == nil {
   279  				return
   280  			}
   281  			glog.Error("Subscription error ", e)
   282  			timer := time.NewTimer(time.Second * 2)
   283  			// try in 2 second interval to resubscribe
   284  			for {
   285  				select {
   286  				case e = <-s.Err():
   287  					if e == nil {
   288  						return
   289  					}
   290  				case <-timer.C:
   291  					ns, err := f()
   292  					if err == nil {
   293  						// subscription successful, restart wait for next error
   294  						s = ns
   295  						continue Loop
   296  					}
   297  					glog.Error("Resubscribe error ", err)
   298  					timer.Reset(time.Second * 2)
   299  				}
   300  			}
   301  		}
   302  	}()
   303  	return nil
   304  }
   305  
   306  func (b *EthereumRPC) closeRPC() {
   307  	if b.newBlockSubscription != nil {
   308  		b.newBlockSubscription.Unsubscribe()
   309  	}
   310  	if b.newTxSubscription != nil {
   311  		b.newTxSubscription.Unsubscribe()
   312  	}
   313  	if b.RPC != nil {
   314  		b.RPC.Close()
   315  	}
   316  }
   317  
   318  func (b *EthereumRPC) reconnectRPC() error {
   319  	glog.Info("Reconnecting RPC")
   320  	b.closeRPC()
   321  	rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	b.RPC = rc
   326  	b.Client = ec
   327  	return b.subscribeEvents()
   328  }
   329  
   330  // Shutdown cleans up rpc interface to ethereum
   331  func (b *EthereumRPC) Shutdown(ctx context.Context) error {
   332  	b.closeRPC()
   333  	b.NewBlock.Close()
   334  	b.NewTx.Close()
   335  	glog.Info("rpc: shutdown")
   336  	return nil
   337  }
   338  
   339  // GetCoinName returns coin name
   340  func (b *EthereumRPC) GetCoinName() string {
   341  	return b.ChainConfig.CoinName
   342  }
   343  
   344  // GetSubversion returns empty string, ethereum does not have subversion
   345  func (b *EthereumRPC) GetSubversion() string {
   346  	return ""
   347  }
   348  
   349  func (b *EthereumRPC) getConsensusVersion() string {
   350  	if b.ChainConfig.ConsensusNodeVersionURL == "" {
   351  		return ""
   352  	}
   353  	httpClient := &http.Client{
   354  		Timeout: 2 * time.Second,
   355  	}
   356  	resp, err := httpClient.Get(b.ChainConfig.ConsensusNodeVersionURL)
   357  	if err != nil || resp.StatusCode != http.StatusOK {
   358  		glog.Error("getConsensusVersion ", err)
   359  		return ""
   360  	}
   361  	body, err := ioutil.ReadAll(resp.Body)
   362  	if err != nil {
   363  		glog.Error("getConsensusVersion ", err)
   364  		return ""
   365  	}
   366  	type consensusVersion struct {
   367  		Data struct {
   368  			Version string `json:"version"`
   369  		} `json:"data"`
   370  	}
   371  	var v consensusVersion
   372  	err = json.Unmarshal(body, &v)
   373  	if err != nil {
   374  		glog.Error("getConsensusVersion ", err)
   375  		return ""
   376  	}
   377  	return v.Data.Version
   378  }
   379  
   380  // GetChainInfo returns information about the connected backend
   381  func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
   382  	h, err := b.getBestHeader()
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   387  	defer cancel()
   388  	id, err := b.Client.NetworkID(ctx)
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	var ver string
   393  	if err := b.RPC.CallContext(ctx, &ver, "web3_clientVersion"); err != nil {
   394  		return nil, err
   395  	}
   396  	consensusVersion := b.getConsensusVersion()
   397  	rv := &bchain.ChainInfo{
   398  		Blocks:           int(h.Number().Int64()),
   399  		Bestblockhash:    h.Hash(),
   400  		Difficulty:       h.Difficulty().String(),
   401  		Version:          ver,
   402  		ConsensusVersion: consensusVersion,
   403  	}
   404  	idi := int(id.Uint64())
   405  	if idi == int(b.MainNetChainID) {
   406  		rv.Chain = "mainnet"
   407  	} else {
   408  		rv.Chain = "testnet " + strconv.Itoa(idi)
   409  	}
   410  	return rv, nil
   411  }
   412  
   413  func (b *EthereumRPC) getBestHeader() (bchain.EVMHeader, error) {
   414  	b.bestHeaderLock.Lock()
   415  	defer b.bestHeaderLock.Unlock()
   416  	// if the best header was not updated for 15 minutes, there could be a subscription problem, reconnect RPC
   417  	// do it only in case of normal operation, not initial synchronization
   418  	if b.bestHeaderTime.Add(15*time.Minute).Before(time.Now()) && !b.bestHeaderTime.IsZero() && b.mempoolInitialized {
   419  		err := b.reconnectRPC()
   420  		if err != nil {
   421  			return nil, err
   422  		}
   423  		b.bestHeader = nil
   424  	}
   425  	if b.bestHeader == nil {
   426  		var err error
   427  		ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   428  		defer cancel()
   429  		b.bestHeader, err = b.Client.HeaderByNumber(ctx, nil)
   430  		if err != nil {
   431  			b.bestHeader = nil
   432  			return nil, err
   433  		}
   434  		b.bestHeaderTime = time.Now()
   435  	}
   436  	return b.bestHeader, nil
   437  }
   438  
   439  // UpdateBestHeader keeps track of the latest block header confirmed on chain
   440  func (b *EthereumRPC) UpdateBestHeader(h bchain.EVMHeader) {
   441  	glog.V(2).Info("rpc: new block header ", h.Number())
   442  	b.bestHeaderLock.Lock()
   443  	b.bestHeader = h
   444  	b.bestHeaderTime = time.Now()
   445  	b.bestHeaderLock.Unlock()
   446  }
   447  
   448  // GetBestBlockHash returns hash of the tip of the best-block-chain
   449  func (b *EthereumRPC) GetBestBlockHash() (string, error) {
   450  	h, err := b.getBestHeader()
   451  	if err != nil {
   452  		return "", err
   453  	}
   454  	return h.Hash(), nil
   455  }
   456  
   457  // GetBestBlockHeight returns height of the tip of the best-block-chain
   458  func (b *EthereumRPC) GetBestBlockHeight() (uint32, error) {
   459  	h, err := b.getBestHeader()
   460  	if err != nil {
   461  		return 0, err
   462  	}
   463  	return uint32(h.Number().Uint64()), nil
   464  }
   465  
   466  // GetBlockHash returns hash of block in best-block-chain at given height
   467  func (b *EthereumRPC) GetBlockHash(height uint32) (string, error) {
   468  	var n big.Int
   469  	n.SetUint64(uint64(height))
   470  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   471  	defer cancel()
   472  	h, err := b.Client.HeaderByNumber(ctx, &n)
   473  	if err != nil {
   474  		if err == ethereum.NotFound {
   475  			return "", bchain.ErrBlockNotFound
   476  		}
   477  		return "", errors.Annotatef(err, "height %v", height)
   478  	}
   479  	return h.Hash(), nil
   480  }
   481  
   482  func (b *EthereumRPC) ethHeaderToBlockHeader(h *rpcHeader) (*bchain.BlockHeader, error) {
   483  	height, err := ethNumber(h.Number)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  	c, err := b.computeConfirmations(uint64(height))
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  	time, err := ethNumber(h.Time)
   492  	if err != nil {
   493  		return nil, err
   494  	}
   495  	size, err := ethNumber(h.Size)
   496  	if err != nil {
   497  		return nil, err
   498  	}
   499  	return &bchain.BlockHeader{
   500  		Hash:          h.Hash,
   501  		Prev:          h.ParentHash,
   502  		Height:        uint32(height),
   503  		Confirmations: int(c),
   504  		Time:          time,
   505  		Size:          int(size),
   506  	}, nil
   507  }
   508  
   509  // GetBlockHeader returns header of block with given hash
   510  func (b *EthereumRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
   511  	raw, err := b.getBlockRaw(hash, 0, false)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	var h rpcHeader
   516  	if err := json.Unmarshal(raw, &h); err != nil {
   517  		return nil, errors.Annotatef(err, "hash %v", hash)
   518  	}
   519  	return b.ethHeaderToBlockHeader(&h)
   520  }
   521  
   522  func (b *EthereumRPC) computeConfirmations(n uint64) (uint32, error) {
   523  	bh, err := b.getBestHeader()
   524  	if err != nil {
   525  		return 0, err
   526  	}
   527  	bn := bh.Number().Uint64()
   528  	// transaction in the best block has 1 confirmation
   529  	return uint32(bn - n + 1), nil
   530  }
   531  
   532  func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (json.RawMessage, error) {
   533  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   534  	defer cancel()
   535  	var raw json.RawMessage
   536  	var err error
   537  	if hash != "" {
   538  		if hash == "pending" {
   539  			err = b.RPC.CallContext(ctx, &raw, "eth_getBlockByNumber", hash, fullTxs)
   540  		} else {
   541  			err = b.RPC.CallContext(ctx, &raw, "eth_getBlockByHash", ethcommon.HexToHash(hash), fullTxs)
   542  		}
   543  	} else {
   544  		err = b.RPC.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), fullTxs)
   545  	}
   546  	if err != nil {
   547  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   548  	} else if len(raw) == 0 || (len(raw) == 4 && string(raw) == "null") {
   549  		return nil, bchain.ErrBlockNotFound
   550  	}
   551  	return raw, nil
   552  }
   553  
   554  func (b *EthereumRPC) processEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, []bchain.AddressAliasRecord, error) {
   555  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   556  	defer cancel()
   557  	var logs []rpcLogWithTxHash
   558  	var ensRecords []bchain.AddressAliasRecord
   559  	err := b.RPC.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{
   560  		"fromBlock": blockNumber,
   561  		"toBlock":   blockNumber,
   562  	})
   563  	if err != nil {
   564  		return nil, nil, errors.Annotatef(err, "eth_getLogs blockNumber %v", blockNumber)
   565  	}
   566  	r := make(map[string][]*bchain.RpcLog)
   567  	for i := range logs {
   568  		l := &logs[i]
   569  		r[l.Hash] = append(r[l.Hash], &l.RpcLog)
   570  		ens := getEnsRecord(l)
   571  		if ens != nil {
   572  			ensRecords = append(ensRecords, *ens)
   573  		}
   574  	}
   575  	return r, ensRecords, nil
   576  }
   577  
   578  type rpcCallTrace struct {
   579  	// CREATE, CREATE2, SELFDESTRUCT, CALL, CALLCODE, DELEGATECALL, STATICCALL
   580  	Type   string         `json:"type"`
   581  	From   string         `json:"from"`
   582  	To     string         `json:"to"`
   583  	Value  string         `json:"value"`
   584  	Error  string         `json:"error"`
   585  	Output string         `json:"output"`
   586  	Calls  []rpcCallTrace `json:"calls"`
   587  }
   588  
   589  type rpcTraceResult struct {
   590  	Result rpcCallTrace `json:"result"`
   591  }
   592  
   593  func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo {
   594  	ci, err := b.fetchContractInfo(contract)
   595  	if ci == nil || err != nil {
   596  		ci = &bchain.ContractInfo{
   597  			Contract: contract,
   598  		}
   599  	}
   600  	ci.Type = bchain.UnknownTokenType
   601  	ci.CreatedInBlock = height
   602  	return ci
   603  }
   604  
   605  func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo {
   606  	value, err := hexutil.DecodeBig(call.Value)
   607  	if call.Type == "CREATE" || call.Type == "CREATE2" {
   608  		d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
   609  			Type:  bchain.CREATE,
   610  			Value: *value,
   611  			From:  call.From,
   612  			To:    call.To, // new contract address
   613  		})
   614  		contracts = append(contracts, *b.getCreationContractInfo(call.To, blockHeight))
   615  	} else if call.Type == "SELFDESTRUCT" {
   616  		d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
   617  			Type:  bchain.SELFDESTRUCT,
   618  			Value: *value,
   619  			From:  call.From, // destroyed contract address
   620  			To:    call.To,
   621  		})
   622  		contracts = append(contracts, bchain.ContractInfo{Contract: call.From, DestructedInBlock: blockHeight})
   623  	} else if call.Type == "DELEGATECALL" {
   624  		// ignore DELEGATECALL (geth v1.11 the changed tracer behavior)
   625  		// 	https://github.com/ethereum/go-ethereum/issues/26726
   626  	} else if err == nil && (value.BitLen() > 0 || b.ChainConfig.ProcessZeroInternalTransactions) {
   627  		d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
   628  			Value: *value,
   629  			From:  call.From,
   630  			To:    call.To,
   631  		})
   632  	}
   633  	if call.Error != "" {
   634  		d.Error = call.Error
   635  	}
   636  	for i := range call.Calls {
   637  		contracts = b.processCallTrace(&call.Calls[i], d, contracts, blockHeight)
   638  	}
   639  	return contracts
   640  }
   641  
   642  // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts
   643  func (b *EthereumRPC) getInternalDataForBlock(blockHash string, blockHeight uint32, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, []bchain.ContractInfo, error) {
   644  	data := make([]bchain.EthereumInternalData, len(transactions))
   645  	contracts := make([]bchain.ContractInfo, 0)
   646  	if ProcessInternalTransactions {
   647  		ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   648  		defer cancel()
   649  		var trace []rpcTraceResult
   650  		err := b.RPC.CallContext(ctx, &trace, "debug_traceBlockByHash", blockHash, map[string]interface{}{"tracer": "callTracer"})
   651  		if err != nil {
   652  			glog.Error("debug_traceBlockByHash block ", blockHash, ", error ", err)
   653  			return data, contracts, err
   654  		}
   655  		if len(trace) != len(data) {
   656  			glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data))
   657  			return data, contracts, err
   658  		}
   659  		for i, result := range trace {
   660  			r := &result.Result
   661  			d := &data[i]
   662  			if r.Type == "CREATE" || r.Type == "CREATE2" {
   663  				d.Type = bchain.CREATE
   664  				d.Contract = r.To
   665  				contracts = append(contracts, *b.getCreationContractInfo(d.Contract, blockHeight))
   666  			} else if r.Type == "SELFDESTRUCT" {
   667  				d.Type = bchain.SELFDESTRUCT
   668  			}
   669  			for j := range r.Calls {
   670  				contracts = b.processCallTrace(&r.Calls[j], d, contracts, blockHeight)
   671  			}
   672  			if r.Error != "" {
   673  				baseError := PackInternalTransactionError(r.Error)
   674  				if len(baseError) > 1 {
   675  					// n, _ := ethNumber(transactions[i].BlockNumber)
   676  					// glog.Infof("Internal Data Error %d %s: unknown base error %s", n, transactions[i].Hash, baseError)
   677  					baseError = strings.ToUpper(baseError[:1]) + baseError[1:] + ". "
   678  				}
   679  				outputError := ParseErrorFromOutput(r.Output)
   680  				if len(outputError) > 0 {
   681  					d.Error = baseError + strings.ToUpper(outputError[:1]) + outputError[1:]
   682  				} else {
   683  					traceError := PackInternalTransactionError(d.Error)
   684  					if traceError == baseError {
   685  						d.Error = baseError
   686  					} else {
   687  						d.Error = baseError + traceError
   688  					}
   689  				}
   690  				// n, _ := ethNumber(transactions[i].BlockNumber)
   691  				// glog.Infof("Internal Data Error %d %s: %s", n, transactions[i].Hash, UnpackInternalTransactionError([]byte(d.Error)))
   692  			}
   693  		}
   694  	}
   695  	return data, contracts, nil
   696  }
   697  
   698  // GetBlock returns block with given hash or height, hash has precedence if both passed
   699  func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
   700  	raw, err := b.getBlockRaw(hash, height, true)
   701  	if err != nil {
   702  		return nil, err
   703  	}
   704  	var head rpcHeader
   705  	if err := json.Unmarshal(raw, &head); err != nil {
   706  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   707  	}
   708  	var body rpcBlockTransactions
   709  	if err := json.Unmarshal(raw, &body); err != nil {
   710  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   711  	}
   712  	bbh, err := b.ethHeaderToBlockHeader(&head)
   713  	if err != nil {
   714  		return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
   715  	}
   716  	// get block events
   717  	// TODO - could be possibly done in parallel to getInternalDataForBlock
   718  	logs, ens, err := b.processEventsForBlock(head.Number)
   719  	if err != nil {
   720  		return nil, err
   721  	}
   722  	// error fetching internal data does not stop the block processing
   723  	var blockSpecificData *bchain.EthereumBlockSpecificData
   724  	internalData, contracts, err := b.getInternalDataForBlock(head.Hash, bbh.Height, body.Transactions)
   725  	// pass internalData error and ENS records in blockSpecificData to be stored
   726  	if err != nil || len(ens) > 0 || len(contracts) > 0 {
   727  		blockSpecificData = &bchain.EthereumBlockSpecificData{}
   728  		if err != nil {
   729  			blockSpecificData.InternalDataError = err.Error()
   730  			// glog.Info("InternalDataError ", bbh.Height, ": ", err.Error())
   731  		}
   732  		if len(ens) > 0 {
   733  			blockSpecificData.AddressAliasRecords = ens
   734  			// glog.Info("ENS", ens)
   735  		}
   736  		if len(contracts) > 0 {
   737  			blockSpecificData.Contracts = contracts
   738  			// glog.Info("Contracts", contracts)
   739  		}
   740  	}
   741  
   742  	btxs := make([]bchain.Tx, len(body.Transactions))
   743  	for i := range body.Transactions {
   744  		tx := &body.Transactions[i]
   745  		btx, err := b.Parser.ethTxToTx(tx, &bchain.RpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true)
   746  		if err != nil {
   747  			return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash)
   748  		}
   749  		btxs[i] = *btx
   750  		if b.mempoolInitialized {
   751  			b.Mempool.RemoveTransactionFromMempool(tx.Hash)
   752  		}
   753  	}
   754  	bbk := bchain.Block{
   755  		BlockHeader:      *bbh,
   756  		Txs:              btxs,
   757  		CoinSpecificData: blockSpecificData,
   758  	}
   759  	return &bbk, nil
   760  }
   761  
   762  // GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
   763  func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
   764  	raw, err := b.getBlockRaw(hash, 0, false)
   765  	if err != nil {
   766  		return nil, err
   767  	}
   768  	var head rpcHeader
   769  	var txs rpcBlockTxids
   770  	if err := json.Unmarshal(raw, &head); err != nil {
   771  		return nil, errors.Annotatef(err, "hash %v", hash)
   772  	}
   773  	if err = json.Unmarshal(raw, &txs); err != nil {
   774  		return nil, err
   775  	}
   776  	bch, err := b.ethHeaderToBlockHeader(&head)
   777  	if err != nil {
   778  		return nil, err
   779  	}
   780  	return &bchain.BlockInfo{
   781  		BlockHeader: *bch,
   782  		Difficulty:  common.JSONNumber(head.Difficulty),
   783  		Nonce:       common.JSONNumber(head.Nonce),
   784  		Txids:       txs.Transactions,
   785  	}, nil
   786  }
   787  
   788  // GetTransactionForMempool returns a transaction by the transaction ID.
   789  // It could be optimized for mempool, i.e. without block time and confirmations
   790  func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
   791  	return b.GetTransaction(txid)
   792  }
   793  
   794  // GetTransaction returns a transaction by the transaction ID.
   795  func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
   796  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   797  	defer cancel()
   798  	var tx *bchain.RpcTransaction
   799  	hash := ethcommon.HexToHash(txid)
   800  	err := b.RPC.CallContext(ctx, &tx, "eth_getTransactionByHash", hash)
   801  	if err != nil {
   802  		return nil, err
   803  	} else if tx == nil {
   804  		if b.mempoolInitialized {
   805  			b.Mempool.RemoveTransactionFromMempool(txid)
   806  		}
   807  		return nil, bchain.ErrTxNotFound
   808  	}
   809  	var btx *bchain.Tx
   810  	if tx.BlockNumber == "" {
   811  		// mempool tx
   812  		btx, err = b.Parser.ethTxToTx(tx, nil, nil, 0, 0, true)
   813  		if err != nil {
   814  			return nil, errors.Annotatef(err, "txid %v", txid)
   815  		}
   816  	} else {
   817  		// non mempool tx - read the block header to get the block time
   818  		raw, err := b.getBlockRaw(tx.BlockHash, 0, false)
   819  		if err != nil {
   820  			return nil, err
   821  		}
   822  		var ht struct {
   823  			Time string `json:"timestamp"`
   824  		}
   825  		if err := json.Unmarshal(raw, &ht); err != nil {
   826  			return nil, errors.Annotatef(err, "hash %v", hash)
   827  		}
   828  		var time int64
   829  		if time, err = ethNumber(ht.Time); err != nil {
   830  			return nil, errors.Annotatef(err, "txid %v", txid)
   831  		}
   832  		var receipt bchain.RpcReceipt
   833  		err = b.RPC.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash)
   834  		if err != nil {
   835  			return nil, errors.Annotatef(err, "txid %v", txid)
   836  		}
   837  		n, err := ethNumber(tx.BlockNumber)
   838  		if err != nil {
   839  			return nil, errors.Annotatef(err, "txid %v", txid)
   840  		}
   841  		confirmations, err := b.computeConfirmations(uint64(n))
   842  		if err != nil {
   843  			return nil, errors.Annotatef(err, "txid %v", txid)
   844  		}
   845  		btx, err = b.Parser.ethTxToTx(tx, &receipt, nil, time, confirmations, true)
   846  		if err != nil {
   847  			return nil, errors.Annotatef(err, "txid %v", txid)
   848  		}
   849  		// remove tx from mempool if it is there
   850  		if b.mempoolInitialized {
   851  			b.Mempool.RemoveTransactionFromMempool(txid)
   852  		}
   853  	}
   854  	return btx, nil
   855  }
   856  
   857  // GetTransactionSpecific returns json as returned by backend, with all coin specific data
   858  func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
   859  	csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData)
   860  	if !ok {
   861  		ntx, err := b.GetTransaction(tx.Txid)
   862  		if err != nil {
   863  			return nil, err
   864  		}
   865  		csd, ok = ntx.CoinSpecificData.(bchain.EthereumSpecificData)
   866  		if !ok {
   867  			return nil, errors.New("Cannot get CoinSpecificData")
   868  		}
   869  	}
   870  	m, err := json.Marshal(&csd)
   871  	return json.RawMessage(m), err
   872  }
   873  
   874  // GetMempoolTransactions returns transactions in mempool
   875  func (b *EthereumRPC) GetMempoolTransactions() ([]string, error) {
   876  	raw, err := b.getBlockRaw("pending", 0, false)
   877  	if err != nil {
   878  		return nil, err
   879  	}
   880  	var body rpcBlockTxids
   881  	if len(raw) > 0 {
   882  		if err := json.Unmarshal(raw, &body); err != nil {
   883  			return nil, err
   884  		}
   885  	}
   886  	return body.Transactions, nil
   887  }
   888  
   889  // EstimateFee returns fee estimation
   890  func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) {
   891  	return b.EstimateSmartFee(blocks, true)
   892  }
   893  
   894  // EstimateSmartFee returns fee estimation
   895  func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
   896  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   897  	defer cancel()
   898  	var r big.Int
   899  	gp, err := b.Client.SuggestGasPrice(ctx)
   900  	if err == nil && b != nil {
   901  		r = *gp
   902  	}
   903  	return r, err
   904  }
   905  
   906  // GetStringFromMap attempts to return the value for a specific key in a map as a string if valid,
   907  // otherwise returns an empty string with false indicating there was no key found, or the value was not a string
   908  func GetStringFromMap(p string, params map[string]interface{}) (string, bool) {
   909  	v, ok := params[p]
   910  	if ok {
   911  		s, ok := v.(string)
   912  		return s, ok
   913  	}
   914  	return "", false
   915  }
   916  
   917  // EthereumTypeEstimateGas returns estimation of gas consumption for given transaction parameters
   918  func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) {
   919  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   920  	defer cancel()
   921  	msg := ethereum.CallMsg{}
   922  	if s, ok := GetStringFromMap("from", params); ok && len(s) > 0 {
   923  		msg.From = ethcommon.HexToAddress(s)
   924  	}
   925  	if s, ok := GetStringFromMap("to", params); ok && len(s) > 0 {
   926  		a := ethcommon.HexToAddress(s)
   927  		msg.To = &a
   928  	}
   929  	if s, ok := GetStringFromMap("data", params); ok && len(s) > 0 {
   930  		msg.Data = ethcommon.FromHex(s)
   931  	}
   932  	if s, ok := GetStringFromMap("value", params); ok && len(s) > 0 {
   933  		msg.Value, _ = hexutil.DecodeBig(s)
   934  	}
   935  	if s, ok := GetStringFromMap("gas", params); ok && len(s) > 0 {
   936  		msg.Gas, _ = hexutil.DecodeUint64(s)
   937  	}
   938  	if s, ok := GetStringFromMap("gasPrice", params); ok && len(s) > 0 {
   939  		msg.GasPrice, _ = hexutil.DecodeBig(s)
   940  	}
   941  	return b.Client.EstimateGas(ctx, msg)
   942  }
   943  
   944  // SendRawTransaction sends raw transaction
   945  func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) {
   946  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   947  	defer cancel()
   948  	var raw json.RawMessage
   949  	err := b.RPC.CallContext(ctx, &raw, "eth_sendRawTransaction", hex)
   950  	if err != nil {
   951  		return "", err
   952  	} else if len(raw) == 0 {
   953  		return "", errors.New("SendRawTransaction: failed")
   954  	}
   955  	var result string
   956  	if err := json.Unmarshal(raw, &result); err != nil {
   957  		return "", errors.Annotatef(err, "raw result %v", raw)
   958  	}
   959  	if result == "" {
   960  		return "", errors.New("SendRawTransaction: failed, empty result")
   961  	}
   962  	return result, nil
   963  }
   964  
   965  // EthereumTypeGetBalance returns current balance of an address
   966  func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) {
   967  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   968  	defer cancel()
   969  	return b.Client.BalanceAt(ctx, addrDesc, nil)
   970  }
   971  
   972  // EthereumTypeGetNonce returns current balance of an address
   973  func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) {
   974  	ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
   975  	defer cancel()
   976  	return b.Client.NonceAt(ctx, addrDesc, nil)
   977  }
   978  
   979  // GetChainParser returns ethereum BlockChainParser
   980  func (b *EthereumRPC) GetChainParser() bchain.BlockChainParser {
   981  	return b.Parser
   982  }