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 }