github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/bchain/coins/btc/bitcoinrpc.go (about)

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