github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/btc/bitcoinrpc.go (about)

     1  package btc
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"io"
     9  	"math/big"
    10  	"net"
    11  	"net/http"
    12  	"runtime/debug"
    13  	"time"
    14  
    15  	"github.com/golang/glog"
    16  	"github.com/juju/errors"
    17  	"github.com/martinboehm/btcd/wire"
    18  	"github.com/trezor/blockbook/bchain"
    19  	"github.com/trezor/blockbook/common"
    20  )
    21  
    22  // BitcoinRPC is an interface to JSON-RPC bitcoind service.
    23  type BitcoinRPC struct {
    24  	*bchain.BaseChain
    25  	client                 http.Client
    26  	rpcURL                 string
    27  	user                   string
    28  	password               string
    29  	Mempool                *bchain.MempoolBitcoinType
    30  	ParseBlocks            bool
    31  	pushHandler            func(bchain.NotificationType)
    32  	mq                     *bchain.MQ
    33  	ChainConfig            *Configuration
    34  	RPCMarshaler           RPCMarshaler
    35  	mempoolGolombFilterP   uint8
    36  	mempoolFilterScripts   string
    37  	mempoolUseZeroedKey    bool
    38  	alternativeFeeProvider alternativeFeeProviderInterface
    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  	RPCUser                      string `json:"rpc_user"`
    47  	RPCPass                      string `json:"rpc_pass"`
    48  	RPCTimeout                   int    `json:"rpc_timeout"`
    49  	AddressAliases               bool   `json:"address_aliases,omitempty"`
    50  	Parse                        bool   `json:"parse"`
    51  	MessageQueueBinding          string `json:"message_queue_binding"`
    52  	Subversion                   string `json:"subversion"`
    53  	BlockAddressesToKeep         int    `json:"block_addresses_to_keep"`
    54  	MempoolWorkers               int    `json:"mempool_workers"`
    55  	MempoolSubWorkers            int    `json:"mempool_sub_workers"`
    56  	AddressFormat                string `json:"address_format"`
    57  	SupportsEstimateFee          bool   `json:"supports_estimate_fee"`
    58  	SupportsEstimateSmartFee     bool   `json:"supports_estimate_smart_fee"`
    59  	XPubMagic                    uint32 `json:"xpub_magic,omitempty"`
    60  	XPubMagicSegwitP2sh          uint32 `json:"xpub_magic_segwit_p2sh,omitempty"`
    61  	XPubMagicSegwitNative        uint32 `json:"xpub_magic_segwit_native,omitempty"`
    62  	Slip44                       uint32 `json:"slip44,omitempty"`
    63  	AlternativeEstimateFee       string `json:"alternative_estimate_fee,omitempty"`
    64  	AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"`
    65  	MinimumCoinbaseConfirmations int    `json:"minimumCoinbaseConfirmations,omitempty"`
    66  	MempoolGolombFilterP         uint8  `json:"mempool_golomb_filter_p,omitempty"`
    67  	MempoolFilterScripts         string `json:"mempool_filter_scripts,omitempty"`
    68  	MempoolFilterUseZeroedKey    bool   `json:"mempool_filter_use_zeroed_key,omitempty"`
    69  }
    70  
    71  // NewBitcoinRPC returns new BitcoinRPC instance.
    72  func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
    73  	var err error
    74  	var c Configuration
    75  	err = json.Unmarshal(config, &c)
    76  	if err != nil {
    77  		return nil, errors.Annotatef(err, "Invalid configuration file")
    78  	}
    79  	// keep at least 100 mappings block->addresses to allow rollback
    80  	if c.BlockAddressesToKeep < 100 {
    81  		c.BlockAddressesToKeep = 100
    82  	}
    83  	// default MinimumCoinbaseConfirmations is 100
    84  	if c.MinimumCoinbaseConfirmations == 0 {
    85  		c.MinimumCoinbaseConfirmations = 100
    86  	}
    87  	// at least 1 mempool worker/subworker for synchronous mempool synchronization
    88  	if c.MempoolWorkers < 1 {
    89  		c.MempoolWorkers = 1
    90  	}
    91  	if c.MempoolSubWorkers < 1 {
    92  		c.MempoolSubWorkers = 1
    93  	}
    94  	// btc supports both calls, other coins overriding BitcoinRPC can change this
    95  	c.SupportsEstimateFee = true
    96  	c.SupportsEstimateSmartFee = true
    97  
    98  	transport := &http.Transport{
    99  		Dial:                (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
   100  		MaxIdleConns:        100,
   101  		MaxIdleConnsPerHost: 100, // necessary to not to deplete ports
   102  	}
   103  
   104  	s := &BitcoinRPC{
   105  		BaseChain:            &bchain.BaseChain{},
   106  		client:               http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
   107  		rpcURL:               c.RPCURL,
   108  		user:                 c.RPCUser,
   109  		password:             c.RPCPass,
   110  		ParseBlocks:          c.Parse,
   111  		ChainConfig:          &c,
   112  		pushHandler:          pushHandler,
   113  		RPCMarshaler:         JSONMarshalerV2{},
   114  		mempoolGolombFilterP: c.MempoolGolombFilterP,
   115  		mempoolFilterScripts: c.MempoolFilterScripts,
   116  		mempoolUseZeroedKey:  c.MempoolFilterUseZeroedKey,
   117  	}
   118  
   119  	return s, nil
   120  }
   121  
   122  // Initialize initializes BitcoinRPC instance.
   123  func (b *BitcoinRPC) Initialize() error {
   124  	b.ChainConfig.SupportsEstimateFee = false
   125  
   126  	ci, err := b.GetChainInfo()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	chainName := ci.Chain
   131  
   132  	params := GetChainParams(chainName)
   133  
   134  	// always create parser
   135  	b.Parser = NewBitcoinParser(params, b.ChainConfig)
   136  
   137  	// parameters for getInfo request
   138  	if params.Net == wire.MainNet {
   139  		b.Testnet = false
   140  		b.Network = "livenet"
   141  	} else {
   142  		b.Testnet = true
   143  		b.Network = "testnet"
   144  	}
   145  
   146  	glog.Info("rpc: block chain ", params.Name)
   147  
   148  	if b.ChainConfig.AlternativeEstimateFee == "whatthefee" {
   149  		if b.alternativeFeeProvider, err = NewWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
   150  			glog.Error("NewWhatTheFee error ", err, " Reverting to default estimateFee functionality")
   151  			// disable AlternativeEstimateFee logic
   152  			b.alternativeFeeProvider = nil
   153  		}
   154  	} else if b.ChainConfig.AlternativeEstimateFee == "mempoolspace" {
   155  		if b.alternativeFeeProvider, err = NewMempoolSpaceFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
   156  			glog.Error("MempoolSpaceFee error ", err, " Reverting to default estimateFee functionality")
   157  			// disable AlternativeEstimateFee logic
   158  			b.alternativeFeeProvider = nil
   159  		}
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  // CreateMempool creates mempool if not already created, however does not initialize it
   166  func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
   167  	if b.Mempool == nil {
   168  		b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.mempoolGolombFilterP, b.mempoolFilterScripts, b.mempoolUseZeroedKey)
   169  	}
   170  	return b.Mempool, nil
   171  }
   172  
   173  // InitializeMempool creates ZeroMQ subscription and sets AddrDescForOutpointFunc to the Mempool
   174  func (b *BitcoinRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error {
   175  	if b.Mempool == nil {
   176  		return errors.New("Mempool not created")
   177  	}
   178  	b.Mempool.AddrDescForOutpoint = addrDescForOutpoint
   179  	b.Mempool.OnNewTxAddr = onNewTxAddr
   180  	b.Mempool.OnNewTx = onNewTx
   181  	if b.mq == nil {
   182  		mq, err := bchain.NewMQ(b.ChainConfig.MessageQueueBinding, b.pushHandler)
   183  		if err != nil {
   184  			glog.Error("mq: ", err)
   185  			return err
   186  		}
   187  		b.mq = mq
   188  	}
   189  	return nil
   190  }
   191  
   192  // Shutdown ZeroMQ and other resources
   193  func (b *BitcoinRPC) Shutdown(ctx context.Context) error {
   194  	if b.mq != nil {
   195  		if err := b.mq.Shutdown(ctx); err != nil {
   196  			glog.Error("MQ.Shutdown error: ", err)
   197  			return err
   198  		}
   199  	}
   200  	return nil
   201  }
   202  
   203  // GetCoinName returns the coin name
   204  func (b *BitcoinRPC) GetCoinName() string {
   205  	return b.ChainConfig.CoinName
   206  }
   207  
   208  // GetSubversion returns the backend subversion
   209  func (b *BitcoinRPC) GetSubversion() string {
   210  	return b.ChainConfig.Subversion
   211  }
   212  
   213  // getblockhash
   214  
   215  type CmdGetBlockHash struct {
   216  	Method string `json:"method"`
   217  	Params struct {
   218  		Height uint32 `json:"height"`
   219  	} `json:"params"`
   220  }
   221  
   222  type ResGetBlockHash struct {
   223  	Error  *bchain.RPCError `json:"error"`
   224  	Result string           `json:"result"`
   225  }
   226  
   227  // getbestblockhash
   228  
   229  type CmdGetBestBlockHash struct {
   230  	Method string `json:"method"`
   231  }
   232  
   233  type ResGetBestBlockHash struct {
   234  	Error  *bchain.RPCError `json:"error"`
   235  	Result string           `json:"result"`
   236  }
   237  
   238  // getblockcount
   239  
   240  type CmdGetBlockCount struct {
   241  	Method string `json:"method"`
   242  }
   243  
   244  type ResGetBlockCount struct {
   245  	Error  *bchain.RPCError `json:"error"`
   246  	Result uint32           `json:"result"`
   247  }
   248  
   249  // getblockchaininfo
   250  
   251  type CmdGetBlockChainInfo struct {
   252  	Method string `json:"method"`
   253  }
   254  
   255  type ResGetBlockChainInfo struct {
   256  	Error  *bchain.RPCError `json:"error"`
   257  	Result struct {
   258  		Chain         string            `json:"chain"`
   259  		Blocks        int               `json:"blocks"`
   260  		Headers       int               `json:"headers"`
   261  		Bestblockhash string            `json:"bestblockhash"`
   262  		Difficulty    common.JSONNumber `json:"difficulty"`
   263  		SizeOnDisk    int64             `json:"size_on_disk"`
   264  		Warnings      string            `json:"warnings"`
   265  	} `json:"result"`
   266  }
   267  
   268  // getnetworkinfo
   269  
   270  type CmdGetNetworkInfo struct {
   271  	Method string `json:"method"`
   272  }
   273  
   274  type ResGetNetworkInfo struct {
   275  	Error  *bchain.RPCError `json:"error"`
   276  	Result struct {
   277  		Version         common.JSONNumber `json:"version"`
   278  		Subversion      common.JSONNumber `json:"subversion"`
   279  		ProtocolVersion common.JSONNumber `json:"protocolversion"`
   280  		Timeoffset      float64           `json:"timeoffset"`
   281  		Warnings        string            `json:"warnings"`
   282  	} `json:"result"`
   283  }
   284  
   285  // getrawmempool
   286  
   287  type CmdGetMempool struct {
   288  	Method string `json:"method"`
   289  }
   290  
   291  type ResGetMempool struct {
   292  	Error  *bchain.RPCError `json:"error"`
   293  	Result []string         `json:"result"`
   294  }
   295  
   296  // getblockheader
   297  
   298  type CmdGetBlockHeader struct {
   299  	Method string `json:"method"`
   300  	Params struct {
   301  		BlockHash string `json:"blockhash"`
   302  		Verbose   bool   `json:"verbose"`
   303  	} `json:"params"`
   304  }
   305  
   306  type ResGetBlockHeader struct {
   307  	Error  *bchain.RPCError   `json:"error"`
   308  	Result bchain.BlockHeader `json:"result"`
   309  }
   310  
   311  // getblock
   312  
   313  type CmdGetBlock struct {
   314  	Method string `json:"method"`
   315  	Params struct {
   316  		BlockHash string `json:"blockhash"`
   317  		Verbosity int    `json:"verbosity"`
   318  	} `json:"params"`
   319  }
   320  
   321  type ResGetBlockRaw struct {
   322  	Error  *bchain.RPCError `json:"error"`
   323  	Result string           `json:"result"`
   324  }
   325  
   326  type BlockThin struct {
   327  	bchain.BlockHeader
   328  	Txids []string `json:"tx"`
   329  }
   330  
   331  type ResGetBlockThin struct {
   332  	Error  *bchain.RPCError `json:"error"`
   333  	Result BlockThin        `json:"result"`
   334  }
   335  
   336  type ResGetBlockFull struct {
   337  	Error  *bchain.RPCError `json:"error"`
   338  	Result bchain.Block     `json:"result"`
   339  }
   340  
   341  type ResGetBlockInfo struct {
   342  	Error  *bchain.RPCError `json:"error"`
   343  	Result bchain.BlockInfo `json:"result"`
   344  }
   345  
   346  // getrawtransaction
   347  
   348  type CmdGetRawTransaction struct {
   349  	Method string `json:"method"`
   350  	Params struct {
   351  		Txid    string `json:"txid"`
   352  		Verbose bool   `json:"verbose"`
   353  	} `json:"params"`
   354  }
   355  
   356  type ResGetRawTransaction struct {
   357  	Error  *bchain.RPCError `json:"error"`
   358  	Result json.RawMessage  `json:"result"`
   359  }
   360  
   361  type ResGetRawTransactionNonverbose struct {
   362  	Error  *bchain.RPCError `json:"error"`
   363  	Result string           `json:"result"`
   364  }
   365  
   366  // estimatesmartfee
   367  
   368  type CmdEstimateSmartFee struct {
   369  	Method string `json:"method"`
   370  	Params struct {
   371  		ConfTarget   int    `json:"conf_target"`
   372  		EstimateMode string `json:"estimate_mode"`
   373  	} `json:"params"`
   374  }
   375  
   376  type ResEstimateSmartFee struct {
   377  	Error  *bchain.RPCError `json:"error"`
   378  	Result struct {
   379  		Feerate common.JSONNumber `json:"feerate"`
   380  		Blocks  int               `json:"blocks"`
   381  	} `json:"result"`
   382  }
   383  
   384  // estimatefee
   385  
   386  type CmdEstimateFee struct {
   387  	Method string `json:"method"`
   388  	Params struct {
   389  		Blocks int `json:"nblocks"`
   390  	} `json:"params"`
   391  }
   392  
   393  type ResEstimateFee struct {
   394  	Error  *bchain.RPCError  `json:"error"`
   395  	Result common.JSONNumber `json:"result"`
   396  }
   397  
   398  // sendrawtransaction
   399  
   400  type CmdSendRawTransaction struct {
   401  	Method string   `json:"method"`
   402  	Params []string `json:"params"`
   403  }
   404  
   405  type ResSendRawTransaction struct {
   406  	Error  *bchain.RPCError `json:"error"`
   407  	Result string           `json:"result"`
   408  }
   409  
   410  // getmempoolentry
   411  
   412  type CmdGetMempoolEntry struct {
   413  	Method string   `json:"method"`
   414  	Params []string `json:"params"`
   415  }
   416  
   417  type ResGetMempoolEntry struct {
   418  	Error  *bchain.RPCError     `json:"error"`
   419  	Result *bchain.MempoolEntry `json:"result"`
   420  }
   421  
   422  // GetBestBlockHash returns hash of the tip of the best-block-chain.
   423  func (b *BitcoinRPC) GetBestBlockHash() (string, error) {
   424  
   425  	glog.V(1).Info("rpc: getbestblockhash")
   426  
   427  	res := ResGetBestBlockHash{}
   428  	req := CmdGetBestBlockHash{Method: "getbestblockhash"}
   429  	err := b.Call(&req, &res)
   430  
   431  	if err != nil {
   432  		return "", err
   433  	}
   434  	if res.Error != nil {
   435  		return "", res.Error
   436  	}
   437  	return res.Result, nil
   438  }
   439  
   440  // GetBestBlockHeight returns height of the tip of the best-block-chain.
   441  func (b *BitcoinRPC) GetBestBlockHeight() (uint32, error) {
   442  	glog.V(1).Info("rpc: getblockcount")
   443  
   444  	res := ResGetBlockCount{}
   445  	req := CmdGetBlockCount{Method: "getblockcount"}
   446  	err := b.Call(&req, &res)
   447  
   448  	if err != nil {
   449  		return 0, err
   450  	}
   451  	if res.Error != nil {
   452  		return 0, res.Error
   453  	}
   454  	return res.Result, nil
   455  }
   456  
   457  // GetChainInfo returns information about the connected backend
   458  func (b *BitcoinRPC) GetChainInfo() (*bchain.ChainInfo, error) {
   459  	glog.V(1).Info("rpc: getblockchaininfo")
   460  
   461  	resCi := ResGetBlockChainInfo{}
   462  	err := b.Call(&CmdGetBlockChainInfo{Method: "getblockchaininfo"}, &resCi)
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  	if resCi.Error != nil {
   467  		return nil, resCi.Error
   468  	}
   469  
   470  	glog.V(1).Info("rpc: getnetworkinfo")
   471  	resNi := ResGetNetworkInfo{}
   472  	err = b.Call(&CmdGetNetworkInfo{Method: "getnetworkinfo"}, &resNi)
   473  	if err != nil {
   474  		return nil, err
   475  	}
   476  	if resNi.Error != nil {
   477  		return nil, resNi.Error
   478  	}
   479  
   480  	rv := &bchain.ChainInfo{
   481  		Bestblockhash: resCi.Result.Bestblockhash,
   482  		Blocks:        resCi.Result.Blocks,
   483  		Chain:         resCi.Result.Chain,
   484  		Difficulty:    string(resCi.Result.Difficulty),
   485  		Headers:       resCi.Result.Headers,
   486  		SizeOnDisk:    resCi.Result.SizeOnDisk,
   487  		Subversion:    string(resNi.Result.Subversion),
   488  		Timeoffset:    resNi.Result.Timeoffset,
   489  	}
   490  	rv.Version = string(resNi.Result.Version)
   491  	rv.ProtocolVersion = string(resNi.Result.ProtocolVersion)
   492  	if len(resCi.Result.Warnings) > 0 {
   493  		rv.Warnings = resCi.Result.Warnings + " "
   494  	}
   495  	if resCi.Result.Warnings != resNi.Result.Warnings {
   496  		rv.Warnings += resNi.Result.Warnings
   497  	}
   498  	return rv, nil
   499  }
   500  
   501  // IsErrBlockNotFound returns true if error means block was not found
   502  func IsErrBlockNotFound(err *bchain.RPCError) bool {
   503  	return err.Message == "Block not found" ||
   504  		err.Message == "Block height out of range"
   505  }
   506  
   507  // GetBlockHash returns hash of block in best-block-chain at given height.
   508  func (b *BitcoinRPC) GetBlockHash(height uint32) (string, error) {
   509  	glog.V(1).Info("rpc: getblockhash ", height)
   510  
   511  	res := ResGetBlockHash{}
   512  	req := CmdGetBlockHash{Method: "getblockhash"}
   513  	req.Params.Height = height
   514  	err := b.Call(&req, &res)
   515  
   516  	if err != nil {
   517  		return "", errors.Annotatef(err, "height %v", height)
   518  	}
   519  	if res.Error != nil {
   520  		if IsErrBlockNotFound(res.Error) {
   521  			return "", bchain.ErrBlockNotFound
   522  		}
   523  		return "", errors.Annotatef(res.Error, "height %v", height)
   524  	}
   525  	return res.Result, nil
   526  }
   527  
   528  // GetBlockHeader returns header of block with given hash.
   529  func (b *BitcoinRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
   530  	glog.V(1).Info("rpc: getblockheader")
   531  
   532  	res := ResGetBlockHeader{}
   533  	req := CmdGetBlockHeader{Method: "getblockheader"}
   534  	req.Params.BlockHash = hash
   535  	req.Params.Verbose = true
   536  	err := b.Call(&req, &res)
   537  
   538  	if err != nil {
   539  		return nil, errors.Annotatef(err, "hash %v", hash)
   540  	}
   541  	if res.Error != nil {
   542  		if IsErrBlockNotFound(res.Error) {
   543  			return nil, bchain.ErrBlockNotFound
   544  		}
   545  		return nil, errors.Annotatef(res.Error, "hash %v", hash)
   546  	}
   547  	return &res.Result, nil
   548  }
   549  
   550  // GetBlock returns block with given hash.
   551  func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
   552  	var err error
   553  	if hash == "" {
   554  		hash, err = b.GetBlockHash(height)
   555  		if err != nil {
   556  			return nil, err
   557  		}
   558  	}
   559  	if !b.ParseBlocks {
   560  		return b.GetBlockFull(hash)
   561  	}
   562  	// optimization
   563  	if height > 0 {
   564  		return b.GetBlockWithoutHeader(hash, height)
   565  	}
   566  	header, err := b.GetBlockHeader(hash)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	data, err := b.GetBlockBytes(hash)
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  	block, err := b.Parser.ParseBlock(data)
   575  	if err != nil {
   576  		return nil, errors.Annotatef(err, "hash %v", hash)
   577  	}
   578  	block.BlockHeader = *header
   579  	return block, nil
   580  }
   581  
   582  // GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
   583  func (b *BitcoinRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
   584  	glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
   585  
   586  	res := ResGetBlockInfo{}
   587  	req := CmdGetBlock{Method: "getblock"}
   588  	req.Params.BlockHash = hash
   589  	req.Params.Verbosity = 1
   590  	err := b.Call(&req, &res)
   591  
   592  	if err != nil {
   593  		return nil, errors.Annotatef(err, "hash %v", hash)
   594  	}
   595  	if res.Error != nil {
   596  		if IsErrBlockNotFound(res.Error) {
   597  			return nil, bchain.ErrBlockNotFound
   598  		}
   599  		return nil, errors.Annotatef(res.Error, "hash %v", hash)
   600  	}
   601  	return &res.Result, nil
   602  }
   603  
   604  // GetBlockWithoutHeader is an optimization - it does not call GetBlockHeader to get prev, next hashes
   605  // instead it sets to header only block hash and height passed in parameters
   606  func (b *BitcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
   607  	data, err := b.GetBlockBytes(hash)
   608  	if err != nil {
   609  		return nil, err
   610  	}
   611  	block, err := b.Parser.ParseBlock(data)
   612  	if err != nil {
   613  		return nil, errors.Annotatef(err, "%v %v", height, hash)
   614  	}
   615  	block.BlockHeader.Hash = hash
   616  	block.BlockHeader.Height = height
   617  	return block, nil
   618  }
   619  
   620  // GetBlockRaw returns block with given hash as hex string
   621  func (b *BitcoinRPC) GetBlockRaw(hash string) (string, error) {
   622  	glog.V(1).Info("rpc: getblock (verbosity=0) ", hash)
   623  
   624  	res := ResGetBlockRaw{}
   625  	req := CmdGetBlock{Method: "getblock"}
   626  	req.Params.BlockHash = hash
   627  	req.Params.Verbosity = 0
   628  	err := b.Call(&req, &res)
   629  
   630  	if err != nil {
   631  		return "", errors.Annotatef(err, "hash %v", hash)
   632  	}
   633  	if res.Error != nil {
   634  		if IsErrBlockNotFound(res.Error) {
   635  			return "", bchain.ErrBlockNotFound
   636  		}
   637  		return "", errors.Annotatef(res.Error, "hash %v", hash)
   638  	}
   639  	return res.Result, nil
   640  }
   641  
   642  // GetBlockBytes returns block with given hash as bytes
   643  func (b *BitcoinRPC) GetBlockBytes(hash string) ([]byte, error) {
   644  	block, err := b.GetBlockRaw(hash)
   645  	if err != nil {
   646  		return nil, err
   647  	}
   648  	return hex.DecodeString(block)
   649  }
   650  
   651  // GetBlockFull returns block with given hash
   652  func (b *BitcoinRPC) GetBlockFull(hash string) (*bchain.Block, error) {
   653  	glog.V(1).Info("rpc: getblock (verbosity=2) ", hash)
   654  
   655  	res := ResGetBlockFull{}
   656  	req := CmdGetBlock{Method: "getblock"}
   657  	req.Params.BlockHash = hash
   658  	req.Params.Verbosity = 2
   659  	err := b.Call(&req, &res)
   660  
   661  	if err != nil {
   662  		return nil, errors.Annotatef(err, "hash %v", hash)
   663  	}
   664  	if res.Error != nil {
   665  		if IsErrBlockNotFound(res.Error) {
   666  			return nil, bchain.ErrBlockNotFound
   667  		}
   668  		return nil, errors.Annotatef(res.Error, "hash %v", hash)
   669  	}
   670  
   671  	for i := range res.Result.Txs {
   672  		tx := &res.Result.Txs[i]
   673  		for j := range tx.Vout {
   674  			vout := &tx.Vout[j]
   675  			// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
   676  			vout.ValueSat, err = b.Parser.AmountToBigInt(vout.JsonValue)
   677  			if err != nil {
   678  				return nil, err
   679  			}
   680  			vout.JsonValue = ""
   681  		}
   682  	}
   683  
   684  	return &res.Result, nil
   685  }
   686  
   687  // GetMempoolTransactions returns transactions in mempool
   688  func (b *BitcoinRPC) GetMempoolTransactions() ([]string, error) {
   689  	glog.V(1).Info("rpc: getrawmempool")
   690  
   691  	res := ResGetMempool{}
   692  	req := CmdGetMempool{Method: "getrawmempool"}
   693  	err := b.Call(&req, &res)
   694  
   695  	if err != nil {
   696  		return nil, err
   697  	}
   698  	if res.Error != nil {
   699  		return nil, res.Error
   700  	}
   701  	return res.Result, nil
   702  }
   703  
   704  // IsMissingTx return true if error means missing tx
   705  func IsMissingTx(err *bchain.RPCError) bool {
   706  	// err.Code == -5 "No such mempool or blockchain transaction"
   707  	return err.Code == -5
   708  }
   709  
   710  // GetTransactionForMempool returns a transaction by the transaction ID
   711  // It could be optimized for mempool, i.e. without block time and confirmations
   712  func (b *BitcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
   713  	glog.V(1).Info("rpc: getrawtransaction nonverbose ", txid)
   714  
   715  	res := ResGetRawTransactionNonverbose{}
   716  	req := CmdGetRawTransaction{Method: "getrawtransaction"}
   717  	req.Params.Txid = txid
   718  	req.Params.Verbose = false
   719  	err := b.Call(&req, &res)
   720  	if err != nil {
   721  		return nil, errors.Annotatef(err, "txid %v", txid)
   722  	}
   723  	if res.Error != nil {
   724  		if IsMissingTx(res.Error) {
   725  			return nil, bchain.ErrTxNotFound
   726  		}
   727  		return nil, errors.Annotatef(res.Error, "txid %v", txid)
   728  	}
   729  	data, err := hex.DecodeString(res.Result)
   730  	if err != nil {
   731  		return nil, errors.Annotatef(err, "txid %v", txid)
   732  	}
   733  	tx, err := b.Parser.ParseTx(data)
   734  	if err != nil {
   735  		return nil, errors.Annotatef(err, "txid %v", txid)
   736  	}
   737  	return tx, nil
   738  }
   739  
   740  // GetTransaction returns a transaction by the transaction ID
   741  func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
   742  	r, err := b.getRawTransaction(txid)
   743  	if err != nil {
   744  		return nil, err
   745  	}
   746  	tx, err := b.Parser.ParseTxFromJson(r)
   747  	if err != nil {
   748  		return nil, errors.Annotatef(err, "txid %v", txid)
   749  	}
   750  	tx.CoinSpecificData = r
   751  	return tx, nil
   752  }
   753  
   754  // GetTransactionSpecific returns json as returned by backend, with all coin specific data
   755  func (b *BitcoinRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
   756  	if csd, ok := tx.CoinSpecificData.(json.RawMessage); ok {
   757  		return csd, nil
   758  	}
   759  	return b.getRawTransaction(tx.Txid)
   760  }
   761  
   762  // getRawTransaction returns json as returned by backend, with all coin specific data
   763  func (b *BitcoinRPC) getRawTransaction(txid string) (json.RawMessage, error) {
   764  	glog.V(1).Info("rpc: getrawtransaction ", txid)
   765  
   766  	res := ResGetRawTransaction{}
   767  	req := CmdGetRawTransaction{Method: "getrawtransaction"}
   768  	req.Params.Txid = txid
   769  	req.Params.Verbose = true
   770  	err := b.Call(&req, &res)
   771  
   772  	if err != nil {
   773  		return nil, errors.Annotatef(err, "txid %v", txid)
   774  	}
   775  	if res.Error != nil {
   776  		if IsMissingTx(res.Error) {
   777  			return nil, bchain.ErrTxNotFound
   778  		}
   779  		return nil, errors.Annotatef(res.Error, "txid %v", txid)
   780  	}
   781  	return res.Result, nil
   782  }
   783  
   784  func (b *BitcoinRPC) blockchainEstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
   785  	// use EstimateFee if EstimateSmartFee is not supported
   786  	if !b.ChainConfig.SupportsEstimateSmartFee && b.ChainConfig.SupportsEstimateFee {
   787  		return b.EstimateFee(blocks)
   788  	}
   789  
   790  	glog.V(1).Info("rpc: estimatesmartfee ", blocks)
   791  
   792  	res := ResEstimateSmartFee{}
   793  	req := CmdEstimateSmartFee{Method: "estimatesmartfee"}
   794  	req.Params.ConfTarget = blocks
   795  	if conservative {
   796  		req.Params.EstimateMode = "CONSERVATIVE"
   797  	} else {
   798  		req.Params.EstimateMode = "ECONOMICAL"
   799  	}
   800  	err := b.Call(&req, &res)
   801  	var r big.Int
   802  	if err != nil {
   803  		return r, err
   804  	}
   805  	if res.Error != nil {
   806  		return r, res.Error
   807  	}
   808  	r, err = b.Parser.AmountToBigInt(res.Result.Feerate)
   809  	if err != nil {
   810  		return r, err
   811  	}
   812  	return r, nil
   813  }
   814  
   815  // EstimateSmartFee returns fee estimation
   816  func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
   817  	// use alternative estimator if enabled
   818  	if b.alternativeFeeProvider != nil {
   819  		r, err := b.alternativeFeeProvider.estimateFee(blocks)
   820  		// in case of error, fallback to default estimator
   821  		if err == nil {
   822  			return r, nil
   823  		}
   824  	}
   825  	return b.blockchainEstimateSmartFee(blocks, conservative)
   826  }
   827  
   828  // EstimateFee returns fee estimation.
   829  func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
   830  	var r big.Int
   831  	var err error
   832  	// use alternative estimator if enabled
   833  	if b.alternativeFeeProvider != nil {
   834  		r, err = b.alternativeFeeProvider.estimateFee(blocks)
   835  		// in case of error, fallback to default estimator
   836  		if err == nil {
   837  			return r, nil
   838  		}
   839  	}
   840  	// use EstimateSmartFee if EstimateFee is not supported
   841  	if !b.ChainConfig.SupportsEstimateFee && b.ChainConfig.SupportsEstimateSmartFee {
   842  		return b.EstimateSmartFee(blocks, true)
   843  	}
   844  
   845  	glog.V(1).Info("rpc: estimatefee ", blocks)
   846  
   847  	res := ResEstimateFee{}
   848  	req := CmdEstimateFee{Method: "estimatefee"}
   849  	req.Params.Blocks = blocks
   850  	err = b.Call(&req, &res)
   851  
   852  	if err != nil {
   853  		return r, err
   854  	}
   855  	if res.Error != nil {
   856  		return r, res.Error
   857  	}
   858  	r, err = b.Parser.AmountToBigInt(res.Result)
   859  	if err != nil {
   860  		return r, err
   861  	}
   862  	return r, nil
   863  }
   864  
   865  // SendRawTransaction sends raw transaction
   866  func (b *BitcoinRPC) SendRawTransaction(tx string) (string, error) {
   867  	glog.V(1).Info("rpc: sendrawtransaction")
   868  
   869  	res := ResSendRawTransaction{}
   870  	req := CmdSendRawTransaction{Method: "sendrawtransaction"}
   871  	req.Params = []string{tx}
   872  	err := b.Call(&req, &res)
   873  
   874  	if err != nil {
   875  		return "", err
   876  	}
   877  	if res.Error != nil {
   878  		return "", res.Error
   879  	}
   880  	return res.Result, nil
   881  }
   882  
   883  // GetMempoolEntry returns mempool data for given transaction
   884  func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
   885  	glog.V(1).Info("rpc: getmempoolentry")
   886  
   887  	res := ResGetMempoolEntry{}
   888  	req := CmdGetMempoolEntry{
   889  		Method: "getmempoolentry",
   890  		Params: []string{txid},
   891  	}
   892  	err := b.Call(&req, &res)
   893  	if err != nil {
   894  		return nil, err
   895  	}
   896  	if res.Error != nil {
   897  		return nil, res.Error
   898  	}
   899  	res.Result.FeeSat, err = b.Parser.AmountToBigInt(res.Result.Fee)
   900  	if err != nil {
   901  		return nil, err
   902  	}
   903  	res.Result.ModifiedFeeSat, err = b.Parser.AmountToBigInt(res.Result.ModifiedFee)
   904  	if err != nil {
   905  		return nil, err
   906  	}
   907  	return res.Result, nil
   908  }
   909  
   910  func safeDecodeResponse(body io.ReadCloser, res interface{}) (err error) {
   911  	var data []byte
   912  	defer func() {
   913  		if r := recover(); r != nil {
   914  			glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data))
   915  			debug.PrintStack()
   916  			if len(data) > 0 && len(data) < 2048 {
   917  				err = errors.Errorf("Error: %v", string(data))
   918  			} else {
   919  				err = errors.New("Internal error")
   920  			}
   921  		}
   922  	}()
   923  	data, err = io.ReadAll(body)
   924  	if err != nil {
   925  		return err
   926  	}
   927  	return json.Unmarshal(data, &res)
   928  }
   929  
   930  // Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request
   931  func (b *BitcoinRPC) Call(req interface{}, res interface{}) error {
   932  	httpData, err := b.RPCMarshaler.Marshal(req)
   933  	if err != nil {
   934  		return err
   935  	}
   936  	httpReq, err := http.NewRequest("POST", b.rpcURL, bytes.NewBuffer(httpData))
   937  	if err != nil {
   938  		return err
   939  	}
   940  	httpReq.SetBasicAuth(b.user, b.password)
   941  	httpRes, err := b.client.Do(httpReq)
   942  	// in some cases the httpRes can contain data even if it returns error
   943  	// see http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
   944  	if httpRes != nil {
   945  		defer httpRes.Body.Close()
   946  	}
   947  	if err != nil {
   948  		return err
   949  	}
   950  	// if server returns HTTP error code it might not return json with response
   951  	// handle both cases
   952  	if httpRes.StatusCode != 200 {
   953  		err = safeDecodeResponse(httpRes.Body, &res)
   954  		if err != nil {
   955  			return errors.Errorf("%v %v", httpRes.Status, err)
   956  		}
   957  		return nil
   958  	}
   959  	return safeDecodeResponse(httpRes.Body, &res)
   960  }