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