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

     1  package dcr
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"math/big"
    11  	"net"
    12  	"net/http"
    13  	"runtime/debug"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  	"time"
    18  
    19  	"github.com/decred/dcrd/dcrjson/v3"
    20  	"github.com/golang/glog"
    21  	"github.com/juju/errors"
    22  	"github.com/trezor/blockbook/bchain"
    23  	"github.com/trezor/blockbook/bchain/coins/btc"
    24  	"github.com/trezor/blockbook/common"
    25  )
    26  
    27  // voteBitYes defines the vote bit set when a given block validates the previous
    28  // block
    29  const voteBitYes = 0x0001
    30  
    31  type DecredRPC struct {
    32  	*btc.BitcoinRPC
    33  	mtx         sync.Mutex
    34  	client      http.Client
    35  	rpcURL      string
    36  	rpcUser     string
    37  	bestBlock   uint32
    38  	rpcPassword string
    39  }
    40  
    41  // NewDecredRPC returns new DecredRPC instance.
    42  func NewDecredRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
    43  	b, err := btc.NewBitcoinRPC(config, pushHandler)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	var c btc.Configuration
    49  	if err = json.Unmarshal(config, &c); err != nil {
    50  		return nil, errors.Annotate(err, "Invalid configuration file")
    51  	}
    52  
    53  	transport := &http.Transport{
    54  		Dial:                (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
    55  		MaxIdleConns:        100,
    56  		MaxIdleConnsPerHost: 100, // necessary to not to deplete ports
    57  		TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
    58  	}
    59  
    60  	d := &DecredRPC{
    61  		BitcoinRPC:  b.(*btc.BitcoinRPC),
    62  		client:      http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
    63  		rpcURL:      c.RPCURL,
    64  		rpcUser:     c.RPCUser,
    65  		rpcPassword: c.RPCPass,
    66  	}
    67  
    68  	d.BitcoinRPC.RPCMarshaler = btc.JSONMarshalerV1{}
    69  	d.BitcoinRPC.ChainConfig.SupportsEstimateSmartFee = false
    70  
    71  	return d, nil
    72  }
    73  
    74  // Initialize initializes DecredRPC instance.
    75  func (d *DecredRPC) Initialize() error {
    76  	chainInfo, err := d.GetChainInfo()
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	chainName := chainInfo.Chain
    82  	glog.Info("Chain name ", chainName)
    83  
    84  	params := GetChainParams(chainName)
    85  
    86  	// always create parser
    87  	d.BitcoinRPC.Parser = NewDecredParser(params, d.BitcoinRPC.ChainConfig)
    88  
    89  	// parameters for getInfo request
    90  	if params.Net == MainnetMagic {
    91  		d.BitcoinRPC.Testnet = false
    92  		d.BitcoinRPC.Network = "livenet"
    93  	} else {
    94  		d.BitcoinRPC.Testnet = true
    95  		d.BitcoinRPC.Network = "testnet"
    96  	}
    97  
    98  	glog.Info("rpc: block chain ", params.Name)
    99  
   100  	return nil
   101  }
   102  
   103  type Error struct {
   104  	Code    int    `json:"code"`
   105  	Message string `json:"message"`
   106  }
   107  
   108  type GenericCmd struct {
   109  	ID     int           `json:"id"`
   110  	Method string        `json:"method"`
   111  	Params []interface{} `json:"params,omitempty"`
   112  }
   113  
   114  type GetBlockChainInfoResult struct {
   115  	Error  Error `json:"error"`
   116  	Result struct {
   117  		Chain                string  `json:"chain"`
   118  		Blocks               int64   `json:"blocks"`
   119  		Headers              int64   `json:"headers"`
   120  		SyncHeight           int64   `json:"syncheight"`
   121  		BestBlockHash        string  `json:"bestblockhash"`
   122  		Difficulty           uint32  `json:"difficulty"`
   123  		VerificationProgress float64 `json:"verificationprogress"`
   124  		ChainWork            string  `json:"chainwork"`
   125  		InitialBlockDownload bool    `json:"initialblockdownload"`
   126  		MaxBlockSize         int64   `json:"maxblocksize"`
   127  	} `json:"result"`
   128  }
   129  
   130  type GetNetworkInfoResult struct {
   131  	Error  Error `json:"error"`
   132  	Result struct {
   133  		Version         int32   `json:"version"`
   134  		ProtocolVersion int32   `json:"protocolversion"`
   135  		TimeOffset      int64   `json:"timeoffset"`
   136  		Connections     int32   `json:"connections"`
   137  		RelayFee        float64 `json:"relayfee"`
   138  	} `json:"result"`
   139  }
   140  
   141  type GetInfoChainResult struct {
   142  	Error  Error `json:"error"`
   143  	Result struct {
   144  		Version         int32   `json:"version"`
   145  		ProtocolVersion int32   `json:"protocolversion"`
   146  		Blocks          int64   `json:"blocks"`
   147  		TimeOffset      int64   `json:"timeoffset"`
   148  		Connections     int32   `json:"connections"`
   149  		Proxy           string  `json:"proxy"`
   150  		Difficulty      float64 `json:"difficulty"`
   151  		TestNet         bool    `json:"testnet"`
   152  		RelayFee        float64 `json:"relayfee"`
   153  		Errors          string  `json:"errors"`
   154  	}
   155  }
   156  
   157  type GetBestBlockResult struct {
   158  	Error  Error `json:"error"`
   159  	Result struct {
   160  		Hash   string `json:"hash"`
   161  		Height uint32 `json:"height"`
   162  	} `json:"result"`
   163  }
   164  
   165  type GetBlockHashResult struct {
   166  	Error  Error  `json:"error"`
   167  	Result string `json:"result"`
   168  }
   169  
   170  type GetBlockResult struct {
   171  	Error  Error `json:"error"`
   172  	Result struct {
   173  		Hash          string            `json:"hash"`
   174  		Confirmations int64             `json:"confirmations"`
   175  		Size          int32             `json:"size"`
   176  		Height        uint32            `json:"height"`
   177  		Version       common.JSONNumber `json:"version"`
   178  		MerkleRoot    string            `json:"merkleroot"`
   179  		StakeRoot     string            `json:"stakeroot"`
   180  		RawTx         []RawTx           `json:"rawtx"`
   181  		Tx            []string          `json:"tx,omitempty"`
   182  		STx           []string          `json:"stx,omitempty"`
   183  		Time          int64             `json:"time"`
   184  		Nonce         common.JSONNumber `json:"nonce"`
   185  		VoteBits      uint16            `json:"votebits"`
   186  		FinalState    string            `json:"finalstate"`
   187  		Voters        uint16            `json:"voters"`
   188  		FreshStake    uint8             `json:"freshstake"`
   189  		Revocations   uint8             `json:"revocations"`
   190  		PoolSize      uint32            `json:"poolsize"`
   191  		Bits          string            `json:"bits"`
   192  		SBits         float64           `json:"sbits"`
   193  		ExtraData     string            `json:"extradata"`
   194  		StakeVersion  uint32            `json:"stakeversion"`
   195  		Difficulty    float64           `json:"difficulty"`
   196  		ChainWork     string            `json:"chainwork"`
   197  		PreviousHash  string            `json:"previousblockhash"`
   198  		NextHash      string            `json:"nextblockhash,omitempty"`
   199  	} `json:"result"`
   200  }
   201  
   202  type GetBlockHeaderResult struct {
   203  	Error  Error `json:"error"`
   204  	Result struct {
   205  		Hash          string            `json:"hash"`
   206  		Confirmations int64             `json:"confirmations"`
   207  		Version       common.JSONNumber `json:"version"`
   208  		MerkleRoot    string            `json:"merkleroot"`
   209  		StakeRoot     string            `json:"stakeroot"`
   210  		VoteBits      uint16            `json:"votebits"`
   211  		FinalState    string            `json:"finalstate"`
   212  		Voters        uint16            `json:"voters"`
   213  		FreshStake    uint8             `json:"freshstake"`
   214  		Revocations   uint8             `json:"revocations"`
   215  		PoolSize      uint32            `json:"poolsize"`
   216  		Bits          string            `json:"bits"`
   217  		SBits         float64           `json:"sbits"`
   218  		Height        uint32            `json:"height"`
   219  		Size          uint32            `json:"size"`
   220  		Time          int64             `json:"time"`
   221  		Nonce         uint32            `json:"nonce"`
   222  		ExtraData     string            `json:"extradata"`
   223  		StakeVersion  uint32            `json:"stakeversion"`
   224  		Difficulty    float64           `json:"difficulty"`
   225  		ChainWork     string            `json:"chainwork"`
   226  		PreviousHash  string            `json:"previousblockhash,omitempty"`
   227  		NextHash      string            `json:"nextblockhash,omitempty"`
   228  	} `json:"result"`
   229  }
   230  
   231  type ScriptSig struct {
   232  	Asm string `json:"asm"`
   233  	Hex string `json:"hex"`
   234  }
   235  
   236  type Vin struct {
   237  	Coinbase    string     `json:"coinbase"`
   238  	Stakebase   string     `json:"stakebase"`
   239  	Txid        string     `json:"txid"`
   240  	Vout        uint32     `json:"vout"`
   241  	Tree        int8       `json:"tree"`
   242  	Sequence    uint32     `json:"sequence"`
   243  	AmountIn    float64    `json:"amountin"`
   244  	BlockHeight uint32     `json:"blockheight"`
   245  	BlockIndex  uint32     `json:"blockindex"`
   246  	ScriptSig   *ScriptSig `json:"scriptsig"`
   247  }
   248  
   249  type ScriptPubKeyResult struct {
   250  	Asm       string   `json:"asm"`
   251  	Hex       string   `json:"hex,omitempty"`
   252  	ReqSigs   int32    `json:"reqSigs,omitempty"`
   253  	Type      string   `json:"type"`
   254  	Addresses []string `json:"addresses,omitempty"`
   255  	CommitAmt *float64 `json:"commitamt,omitempty"`
   256  }
   257  
   258  type Vout struct {
   259  	Value        float64            `json:"value"`
   260  	N            uint32             `json:"n"`
   261  	Version      uint16             `json:"version"`
   262  	ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
   263  }
   264  
   265  type RawTx struct {
   266  	Hex           string `json:"hex"`
   267  	Txid          string `json:"txid"`
   268  	Version       int32  `json:"version"`
   269  	LockTime      uint32 `json:"locktime"`
   270  	Vin           []Vin  `json:"vin"`
   271  	Vout          []Vout `json:"vout"`
   272  	Expiry        uint32 `json:"expiry"`
   273  	BlockIndex    uint32 `json:"blockindex,omitempty"`
   274  	Confirmations int64  `json:"confirmations,omitempty"`
   275  	Time          int64  `json:"time,omitempty"`
   276  	Blocktime     int64  `json:"blocktime,omitempty"`
   277  	TxExtraInfo
   278  }
   279  
   280  type GetTransactionResult struct {
   281  	Error  Error `json:"error"`
   282  	Result struct {
   283  		RawTx
   284  	} `json:"result"`
   285  }
   286  
   287  type MempoolTxsResult struct {
   288  	Error  Error    `json:"error"`
   289  	Result []string `json:"result"`
   290  }
   291  
   292  type EstimateSmartFeeResult struct {
   293  	Error  Error `json:"error"`
   294  	Result struct {
   295  		FeeRate float64  `json:"feerate"`
   296  		Errors  []string `json:"errors"`
   297  		Blocks  int64    `json:"blocks"`
   298  	} `json:"result"`
   299  }
   300  
   301  type EstimateFeeResult struct {
   302  	Error  Error             `json:"error"`
   303  	Result common.JSONNumber `json:"result"`
   304  }
   305  
   306  type SendRawTransactionResult struct {
   307  	Error  Error  `json:"error"`
   308  	Result string `json:"result"`
   309  }
   310  
   311  type DecodeRawTransactionResult struct {
   312  	Error  Error `json:"error"`
   313  	Result struct {
   314  		Txid     string `json:"txid"`
   315  		Version  int32  `json:"version"`
   316  		Locktime uint32 `json:"locktime"`
   317  		Expiry   uint32 `json:"expiry"`
   318  		Vin      []Vin  `json:"vin"`
   319  		Vout     []Vout `json:"vout"`
   320  		TxExtraInfo
   321  	} `json:"result"`
   322  }
   323  
   324  type TxExtraInfo struct {
   325  	BlockHeight uint32 `json:"blockheight,omitempty"`
   326  	BlockHash   string `json:"blockhash,omitempty"`
   327  }
   328  
   329  func (d *DecredRPC) GetChainInfo() (*bchain.ChainInfo, error) {
   330  	blockchainInfoRequest := GenericCmd{
   331  		ID:     1,
   332  		Method: "getblockchaininfo",
   333  	}
   334  
   335  	var blockchainInfoResult GetBlockChainInfoResult
   336  	if err := d.Call(blockchainInfoRequest, &blockchainInfoResult); err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	if blockchainInfoResult.Error.Message != "" {
   341  		return nil, mapToStandardErr("Error fetching blockchain info: %s", blockchainInfoResult.Error)
   342  	}
   343  
   344  	infoChainRequest := GenericCmd{
   345  		ID:     2,
   346  		Method: "getinfo",
   347  	}
   348  
   349  	var infoChainResult GetInfoChainResult
   350  	if err := d.Call(infoChainRequest, &infoChainResult); err != nil {
   351  		return nil, err
   352  	}
   353  
   354  	if infoChainResult.Error.Message != "" {
   355  		return nil, mapToStandardErr("Error fetching network info: %s", infoChainResult.Error)
   356  	}
   357  
   358  	chainInfo := &bchain.ChainInfo{
   359  		Chain:           blockchainInfoResult.Result.Chain,
   360  		Blocks:          int(blockchainInfoResult.Result.Blocks),
   361  		Headers:         int(blockchainInfoResult.Result.Headers),
   362  		Bestblockhash:   blockchainInfoResult.Result.BestBlockHash,
   363  		Difficulty:      strconv.Itoa(int(blockchainInfoResult.Result.Difficulty)),
   364  		SizeOnDisk:      blockchainInfoResult.Result.SyncHeight,
   365  		Version:         strconv.Itoa(int(infoChainResult.Result.Version)),
   366  		Subversion:      "",
   367  		ProtocolVersion: strconv.Itoa(int(infoChainResult.Result.ProtocolVersion)),
   368  		Timeoffset:      float64(infoChainResult.Result.TimeOffset),
   369  		Warnings:        "",
   370  	}
   371  	return chainInfo, nil
   372  }
   373  
   374  // getChainBestBlock returns the best block according to dcrd chain. This block
   375  // has no atleast one confirming block.
   376  func (d *DecredRPC) getChainBestBlock() (*GetBestBlockResult, error) {
   377  	bestBlockRequest := GenericCmd{
   378  		ID:     1,
   379  		Method: "getbestblock",
   380  	}
   381  
   382  	var bestBlockResult GetBestBlockResult
   383  	if err := d.Call(bestBlockRequest, &bestBlockResult); err != nil {
   384  		return nil, err
   385  	}
   386  
   387  	if bestBlockResult.Error.Message != "" {
   388  		return nil, mapToStandardErr("Error fetching best block: %s", bestBlockResult.Error)
   389  	}
   390  
   391  	return &bestBlockResult, nil
   392  }
   393  
   394  // getBestBlock returns details for the block mined immediately before the
   395  // official dcrd chain's bestblock i.e. it has a minimum of 1 confirmation.
   396  // The chain's best block is not returned as its block validity is not guarranteed.
   397  func (d *DecredRPC) getBestBlock() (*GetBestBlockResult, error) {
   398  	bestBlockResult, err := d.getChainBestBlock()
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  
   403  	// remove the block with less than 1 confirming block
   404  	bestBlockResult.Result.Height--
   405  	validBlockHash, err := d.getBlockHashByHeight(bestBlockResult.Result.Height)
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  
   410  	bestBlockResult.Result.Hash = validBlockHash.Result
   411  
   412  	return bestBlockResult, nil
   413  }
   414  
   415  // GetBestBlockHash returns the block hash of the most recent block to be mined
   416  // and has a minimum of 1 confirming block.
   417  func (d *DecredRPC) GetBestBlockHash() (string, error) {
   418  	bestBlock, err := d.getBestBlock()
   419  	if err != nil {
   420  		return "", err
   421  	}
   422  
   423  	return bestBlock.Result.Hash, nil
   424  }
   425  
   426  // GetBestBlockHeight returns the block height of the most recent block to be mined
   427  // and has a minimum of 1 confirming block.
   428  func (d *DecredRPC) GetBestBlockHeight() (uint32, error) {
   429  	bestBlock, err := d.getBestBlock()
   430  	if err != nil {
   431  		return 0, err
   432  	}
   433  
   434  	return uint32(bestBlock.Result.Height), err
   435  }
   436  
   437  // GetBlockHash returns the block hash of the block at the provided height.
   438  func (d *DecredRPC) GetBlockHash(height uint32) (string, error) {
   439  	blockHashResult, err := d.getBlockHashByHeight(height)
   440  	if err != nil {
   441  		return "", err
   442  	}
   443  
   444  	return blockHashResult.Result, nil
   445  }
   446  
   447  func (d *DecredRPC) getBlockHashByHeight(height uint32) (*GetBlockHashResult, error) {
   448  	blockHashRequest := GenericCmd{
   449  		ID:     1,
   450  		Method: "getblockhash",
   451  		Params: []interface{}{height},
   452  	}
   453  
   454  	var blockHashResult GetBlockHashResult
   455  	if err := d.Call(blockHashRequest, &blockHashResult); err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	if blockHashResult.Error.Message != "" {
   460  		return nil, mapToStandardErr("Error fetching block hash: %s", blockHashResult.Error)
   461  	}
   462  
   463  	return &blockHashResult, nil
   464  }
   465  
   466  // GetBlockHeader returns the block header of the block the provided block hash.
   467  func (d *DecredRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
   468  	blockHeaderRequest := GenericCmd{
   469  		ID:     1,
   470  		Method: "getblockheader",
   471  		Params: []interface{}{hash},
   472  	}
   473  
   474  	var blockHeader GetBlockHeaderResult
   475  	if err := d.Call(blockHeaderRequest, &blockHeader); err != nil {
   476  		return nil, err
   477  	}
   478  
   479  	if blockHeader.Error.Message != "" {
   480  		return nil, mapToStandardErr("Error fetching block info: %s", blockHeader.Error)
   481  	}
   482  
   483  	header := &bchain.BlockHeader{
   484  		Hash:          blockHeader.Result.Hash,
   485  		Prev:          blockHeader.Result.PreviousHash,
   486  		Next:          blockHeader.Result.NextHash,
   487  		Height:        blockHeader.Result.Height,
   488  		Confirmations: int(blockHeader.Result.Confirmations),
   489  		Size:          int(blockHeader.Result.Size),
   490  		Time:          blockHeader.Result.Time,
   491  	}
   492  
   493  	return header, nil
   494  }
   495  
   496  // GetBlock returns the block retrieved using the provided block hash by default
   497  // or using the block height if an empty hash string was provided. If the
   498  // requested block has less than 2 confirmation bchain.ErrBlockNotFound error
   499  // is returned. This rule is in places to guarrantee that only validated block
   500  // details (txs) are saved to the db. Access to the bestBlock height is threadsafe.
   501  func (d *DecredRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
   502  	// Confirm if the block at provided height has at least 2 confirming blocks.
   503  	d.mtx.Lock()
   504  	if height > d.bestBlock {
   505  		bestBlock, err := d.getBestBlock()
   506  		if err != nil || height > bestBlock.Result.Height {
   507  			// If an error occurred or the current height doesn't have a minimum
   508  			// of two confirming blocks (greater than best block), quit.
   509  			d.mtx.Unlock()
   510  			return nil, bchain.ErrBlockNotFound
   511  		}
   512  
   513  		d.bestBlock = bestBlock.Result.Height
   514  	}
   515  	d.mtx.Unlock() // Releases the lock soonest possible
   516  
   517  	if hash == "" {
   518  		getHashResult, err := d.getBlockHashByHeight(height)
   519  		if err != nil {
   520  			return nil, err
   521  		}
   522  		hash = getHashResult.Result
   523  	}
   524  
   525  	block, err := d.getBlock(hash)
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  
   530  	header := bchain.BlockHeader{
   531  		Hash:          block.Result.Hash,
   532  		Prev:          block.Result.PreviousHash,
   533  		Next:          block.Result.NextHash,
   534  		Height:        block.Result.Height,
   535  		Confirmations: int(block.Result.Confirmations),
   536  		Size:          int(block.Result.Size),
   537  		Time:          block.Result.Time,
   538  	}
   539  
   540  	bchainBlock := &bchain.Block{BlockHeader: header}
   541  
   542  	// Check the current block validity by fetch the next block
   543  	nextBlockHashResult, err := d.getBlockHashByHeight(height + 1)
   544  	if err != nil {
   545  		return nil, err
   546  	}
   547  
   548  	nextBlock, err := d.getBlock(nextBlockHashResult.Result)
   549  	if err != nil {
   550  		return nil, err
   551  	}
   552  
   553  	// If the Votesbits set equals to voteBitYes append the regular transactions.
   554  	if nextBlock.Result.VoteBits == voteBitYes {
   555  		for _, txID := range block.Result.Tx {
   556  			if block.Result.Height == 0 {
   557  				continue
   558  			}
   559  
   560  			tx, err := d.GetTransaction(txID)
   561  			if err != nil {
   562  				return nil, err
   563  			}
   564  
   565  			bchainBlock.Txs = append(bchainBlock.Txs, *tx)
   566  		}
   567  	}
   568  
   569  	return bchainBlock, nil
   570  }
   571  
   572  func (d *DecredRPC) getBlock(hash string) (*GetBlockResult, error) {
   573  	blockRequest := GenericCmd{
   574  		ID:     1,
   575  		Method: "getblock",
   576  		Params: []interface{}{hash},
   577  	}
   578  
   579  	var block GetBlockResult
   580  
   581  	//Need for skip block height 0 without data
   582  	if hash == "298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980" {
   583  		glog.Info("Skip 0 block with hash " + hash)
   584  		return &block, nil
   585  	}
   586  
   587  	if err := d.Call(blockRequest, &block); err != nil {
   588  		return nil, err
   589  	}
   590  
   591  	if block.Error.Message != "" {
   592  		return nil, mapToStandardErr("Error fetching block info: %s", block.Error)
   593  	}
   594  
   595  	return &block, nil
   596  }
   597  
   598  func (d *DecredRPC) decodeRawTransaction(txHex string) (*bchain.Tx, error) {
   599  	decodeRawTxRequest := GenericCmd{
   600  		ID:     1,
   601  		Method: "decoderawtransaction",
   602  		Params: []interface{}{txHex},
   603  	}
   604  
   605  	var decodeRawTxResult DecodeRawTransactionResult
   606  	if err := d.Call(decodeRawTxRequest, &decodeRawTxResult); err != nil {
   607  		return nil, err
   608  	}
   609  
   610  	if decodeRawTxResult.Error.Message != "" {
   611  		return nil, mapToStandardErr("Error decoding raw tx: %s", decodeRawTxResult.Error)
   612  	}
   613  
   614  	tx := &bchain.Tx{
   615  		Hex:      txHex,
   616  		Txid:     decodeRawTxResult.Result.Txid,
   617  		Version:  decodeRawTxResult.Result.Version,
   618  		LockTime: decodeRawTxResult.Result.Locktime,
   619  	}
   620  
   621  	// Add block height and block hash info
   622  	tx.CoinSpecificData = decodeRawTxResult.Result.TxExtraInfo
   623  
   624  	return tx, nil
   625  }
   626  
   627  func (d *DecredRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
   628  	block, err := d.getBlock(hash)
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  
   633  	header := bchain.BlockHeader{
   634  		Hash:          block.Result.Hash,
   635  		Prev:          block.Result.PreviousHash,
   636  		Next:          block.Result.NextHash,
   637  		Height:        block.Result.Height,
   638  		Confirmations: int(block.Result.Confirmations),
   639  		Size:          int(block.Result.Size),
   640  		Time:          int64(block.Result.Time),
   641  	}
   642  
   643  	bInfo := &bchain.BlockInfo{
   644  		BlockHeader: header,
   645  		MerkleRoot:  block.Result.MerkleRoot,
   646  		Version:     block.Result.Version,
   647  		Nonce:       block.Result.Nonce,
   648  		Bits:        block.Result.Bits,
   649  		Difficulty:  common.JSONNumber(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)),
   650  		Txids:       block.Result.Tx,
   651  	}
   652  
   653  	return bInfo, nil
   654  }
   655  
   656  // GetTransaction returns a transaction by the transaction ID
   657  func (d *DecredRPC) GetTransaction(txid string) (*bchain.Tx, error) {
   658  	r, err := d.getRawTransaction(txid)
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  
   663  	tx, err := d.Parser.ParseTxFromJson(r)
   664  	if err != nil {
   665  		return nil, errors.Annotatef(err, "txid %v", txid)
   666  	}
   667  
   668  	return tx, nil
   669  }
   670  
   671  // getRawTransaction returns json as returned by backend, with all coin specific data
   672  func (d *DecredRPC) getRawTransaction(txid string) (json.RawMessage, error) {
   673  	if txid == "" {
   674  		return nil, bchain.ErrTxidMissing
   675  	}
   676  
   677  	verbose := 1
   678  	getTxRequest := GenericCmd{
   679  		ID:     1,
   680  		Method: "getrawtransaction",
   681  		Params: []interface{}{txid, &verbose},
   682  	}
   683  
   684  	var getTxResult GetTransactionResult
   685  	if err := d.Call(getTxRequest, &getTxResult); err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	if getTxResult.Error.Message != "" {
   690  		return nil, mapToStandardErr("Error fetching transaction: %s", getTxResult.Error)
   691  	}
   692  
   693  	bytes, err := json.Marshal(getTxResult.Result)
   694  	if err != nil {
   695  		return nil, errors.Annotatef(err, "txid %v", txid)
   696  	}
   697  
   698  	return json.RawMessage(bytes), nil
   699  }
   700  
   701  // GetTransactionForMempool returns the full tx information identified by the
   702  // provided txid.
   703  func (d *DecredRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
   704  	return d.GetTransaction(txid)
   705  }
   706  
   707  // GetMempoolTransactions returns a slice of regular transactions currently in
   708  // the mempool. The block whose validation is still undecided will have its txs,
   709  // listed like they are still in the mempool till the block is confirmed.
   710  func (d *DecredRPC) GetMempoolTransactions() ([]string, error) {
   711  	verbose := false
   712  	txType := "regular"
   713  	mempoolRequest := GenericCmd{
   714  		ID:     1,
   715  		Method: "getrawmempool",
   716  		Params: []interface{}{&verbose, &txType},
   717  	}
   718  
   719  	var mempool MempoolTxsResult
   720  	if err := d.Call(mempoolRequest, &mempool); err != nil {
   721  		return []string{}, err
   722  	}
   723  
   724  	if mempool.Error.Message != "" {
   725  		return nil, mapToStandardErr("Error fetching mempool data: %s", mempool.Error)
   726  	}
   727  
   728  	unvalidatedBlockResult, err := d.getChainBestBlock()
   729  	if err != nil {
   730  		return nil, err
   731  	}
   732  
   733  	unvalidatedBlock, err := d.getBlock(unvalidatedBlockResult.Result.Hash)
   734  	if err != nil {
   735  		return nil, err
   736  	}
   737  
   738  	mempool.Result = append(mempool.Result, unvalidatedBlock.Result.Tx...)
   739  
   740  	return mempool.Result, nil
   741  }
   742  
   743  // GetTransactionSpecific returns the json raw message for the tx identified by
   744  // the provided txid.
   745  func (d *DecredRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
   746  	return d.getRawTransaction(tx.Txid)
   747  }
   748  
   749  // EstimateSmartFee returns fee estimation
   750  func (d *DecredRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
   751  	estimateSmartFeeRequest := GenericCmd{
   752  		ID:     1,
   753  		Method: "estimatesmartfee",
   754  		Params: []interface{}{blocks},
   755  	}
   756  
   757  	var smartFeeEstimate EstimateSmartFeeResult
   758  	if err := d.Call(estimateSmartFeeRequest, &smartFeeEstimate); err != nil {
   759  		return *big.NewInt(0), err
   760  	}
   761  
   762  	if smartFeeEstimate.Error.Message != "" {
   763  		return *big.NewInt(0), mapToStandardErr("Error fetching smart fee estimate: %s", smartFeeEstimate.Error)
   764  	}
   765  
   766  	return *big.NewInt(int64(smartFeeEstimate.Result.FeeRate)), nil
   767  }
   768  
   769  // EstimateFee returns fee estimation.
   770  func (d *DecredRPC) EstimateFee(blocks int) (big.Int, error) {
   771  	estimateFeeRequest := GenericCmd{
   772  		ID:     1,
   773  		Method: "estimatefee",
   774  		Params: []interface{}{blocks},
   775  	}
   776  
   777  	var feeEstimate EstimateFeeResult
   778  	if err := d.Call(estimateFeeRequest, &feeEstimate); err != nil {
   779  		return *big.NewInt(0), err
   780  	}
   781  
   782  	if feeEstimate.Error.Message != "" {
   783  		return *big.NewInt(0), mapToStandardErr("Error fetching fee estimate: %s", feeEstimate.Error)
   784  	}
   785  
   786  	r, err := d.Parser.AmountToBigInt(feeEstimate.Result)
   787  	if err != nil {
   788  		return r, err
   789  	}
   790  
   791  	return r, nil
   792  }
   793  
   794  func (d *DecredRPC) SendRawTransaction(tx string) (string, error) {
   795  	sendRawTxRequest := &GenericCmd{
   796  		ID:     1,
   797  		Method: "sendrawtransaction",
   798  		Params: []interface{}{tx},
   799  	}
   800  
   801  	var sendRawTxResult SendRawTransactionResult
   802  	err := d.Call(sendRawTxRequest, &sendRawTxResult)
   803  	if err != nil {
   804  		return "", err
   805  	}
   806  
   807  	if sendRawTxResult.Error.Message != "" {
   808  		return "", mapToStandardErr("error sending raw transaction: %s", sendRawTxResult.Error)
   809  	}
   810  
   811  	return sendRawTxResult.Result, nil
   812  }
   813  
   814  // Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request
   815  func (d *DecredRPC) Call(req interface{}, res interface{}) error {
   816  	httpData, err := json.Marshal(req)
   817  	if err != nil {
   818  		return err
   819  	}
   820  
   821  	httpReq, err := http.NewRequest("POST", d.rpcURL, bytes.NewBuffer(httpData))
   822  	if err != nil {
   823  		return err
   824  	}
   825  	httpReq.SetBasicAuth(d.rpcUser, d.rpcPassword)
   826  	httpRes, err := d.client.Do(httpReq)
   827  	// in some cases the httpRes can contain data even if it returns error
   828  	// see http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
   829  	if httpRes != nil {
   830  		defer httpRes.Body.Close()
   831  	}
   832  	if err != nil {
   833  		return err
   834  	}
   835  
   836  	// if server returns HTTP error code it might not return json with response
   837  	// handle both cases
   838  	if httpRes.StatusCode != 200 {
   839  		if err = safeDecodeResponse(httpRes.Body, &res); err != nil {
   840  			return errors.Errorf("%v %v", httpRes.Status, err)
   841  		}
   842  		return nil
   843  	}
   844  	return safeDecodeResponse(httpRes.Body, &res)
   845  }
   846  
   847  func safeDecodeResponse(body io.ReadCloser, res *interface{}) (err error) {
   848  	var data []byte
   849  	defer func() {
   850  		if r := recover(); r != nil {
   851  			glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data))
   852  			debug.PrintStack()
   853  			if len(data) > 0 && len(data) < 2048 {
   854  				err = errors.Errorf("Error: %v", string(data))
   855  			} else {
   856  				err = errors.New("Internal error")
   857  			}
   858  		}
   859  	}()
   860  	data, err = ioutil.ReadAll(body)
   861  	if err != nil {
   862  		return err
   863  	}
   864  
   865  	error := json.Unmarshal(data, res)
   866  	return error
   867  }
   868  
   869  // mapToStandardErr map the dcrd API Message errors to the standard error messages
   870  // supported by trezor. Dcrd errors to be mapped are listed here:
   871  // https://github.com/decred/dcrd/blob/2f5e47371263b996bb99e8dc3484f659309bd83a/dcrjson/jsonerr.go
   872  func mapToStandardErr(customPrefix string, err Error) error {
   873  	switch {
   874  	case strings.Contains(err.Message, dcrjson.ErrBlockNotFound.Message) || // Block not found
   875  		strings.Contains(err.Message, dcrjson.ErrOutOfRange.Message) || // Block number out of range
   876  		strings.Contains(err.Message, dcrjson.ErrBestBlockHash.Message): // Error getting best block hash
   877  		return bchain.ErrBlockNotFound
   878  	case strings.Contains(err.Message, dcrjson.ErrNoTxInfo.Message): // No information available about transaction
   879  		return bchain.ErrTxNotFound
   880  	case strings.Contains(err.Message, dcrjson.ErrInvalidTxVout.Message): // Output index number (vout) does not exist for transaction
   881  		return bchain.ErrTxidMissing
   882  	default:
   883  		return fmt.Errorf(customPrefix, err.Message)
   884  	}
   885  }