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