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