github.com/cryptohub-digital/blockbook@v0.3.5-0.20240403155730-99ab40b9104c/bchain/coins/xcb/xcbrpc.go (about)

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