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