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