1# Copyright (C) 2007 Jan-Klaas Kollhof 2# Copyright (C) 2011-2018 The python-bitcoinlib developers 3# 4# This file is part of python-bitcoinlib. 5# 6# It is subject to the license terms in the LICENSE file found in the top-level 7# directory of this distribution. 8# 9# No part of python-bitcoinlib, including this file, may be copied, modified, 10# propagated, or distributed except according to the terms contained in the 11# LICENSE file. 12 13"""Bitcoin Core RPC support 14 15By default this uses the standard library ``json`` module. By monkey patching, 16a different implementation can be used instead, at your own risk: 17 18>>> import simplejson 19>>> import bitcoin.rpc 20>>> bitcoin.rpc.json = simplejson 21 22(``simplejson`` is the externally maintained version of the same module and 23thus better optimized but perhaps less stable.) 24""" 25 26from __future__ import absolute_import, division, print_function, unicode_literals 27import ssl 28 29try: 30 import http.client as httplib 31except ImportError: 32 import httplib 33import base64 34import binascii 35import decimal 36import json 37import os 38import platform 39import sys 40try: 41 import urllib.parse as urlparse 42except ImportError: 43 import urlparse 44 45import bitcoin 46from bitcoin.core import COIN, x, lx, b2lx, CBlock, CBlockHeader, CTransaction, COutPoint, CTxOut 47from bitcoin.core.script import CScript 48from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret 49 50DEFAULT_USER_AGENT = "AuthServiceProxy/0.1" 51 52DEFAULT_HTTP_TIMEOUT = 30 53 54# (un)hexlify to/from unicode, needed for Python3 55unhexlify = binascii.unhexlify 56hexlify = binascii.hexlify 57if sys.version > '3': 58 unhexlify = lambda h: binascii.unhexlify(h.encode('utf8')) 59 hexlify = lambda b: binascii.hexlify(b).decode('utf8') 60 61 62class JSONRPCError(Exception): 63 """JSON-RPC protocol error base class 64 65 Subclasses of this class also exist for specific types of errors; the set 66 of all subclasses is by no means complete. 67 """ 68 69 SUBCLS_BY_CODE = {} 70 71 @classmethod 72 def _register_subcls(cls, subcls): 73 cls.SUBCLS_BY_CODE[subcls.RPC_ERROR_CODE] = subcls 74 return subcls 75 76 def __new__(cls, rpc_error): 77 assert cls is JSONRPCError 78 cls = JSONRPCError.SUBCLS_BY_CODE.get(rpc_error['code'], cls) 79 80 self = Exception.__new__(cls) 81 82 super(JSONRPCError, self).__init__( 83 'msg: %r code: %r' % 84 (rpc_error['message'], rpc_error['code'])) 85 self.error = rpc_error 86 87 return self 88 89@JSONRPCError._register_subcls 90class ForbiddenBySafeModeError(JSONRPCError): 91 RPC_ERROR_CODE = -2 92 93@JSONRPCError._register_subcls 94class InvalidAddressOrKeyError(JSONRPCError): 95 RPC_ERROR_CODE = -5 96 97@JSONRPCError._register_subcls 98class InvalidParameterError(JSONRPCError): 99 RPC_ERROR_CODE = -8 100 101@JSONRPCError._register_subcls 102class VerifyError(JSONRPCError): 103 RPC_ERROR_CODE = -25 104 105@JSONRPCError._register_subcls 106class VerifyRejectedError(JSONRPCError): 107 RPC_ERROR_CODE = -26 108 109@JSONRPCError._register_subcls 110class VerifyAlreadyInChainError(JSONRPCError): 111 RPC_ERROR_CODE = -27 112 113@JSONRPCError._register_subcls 114class InWarmupError(JSONRPCError): 115 RPC_ERROR_CODE = -28 116 117 118class BaseProxy(object): 119 """Base JSON-RPC proxy class. Contains only private methods; do not use 120 directly.""" 121 122 def __init__(self, 123 service_url=None, 124 service_port=None, 125 btc_conf_file=None, 126 timeout=DEFAULT_HTTP_TIMEOUT, 127 connection=None): 128 129 # Create a dummy connection early on so if __init__() fails prior to 130 # __conn being created __del__() can detect the condition and handle it 131 # correctly. 132 self.__conn = None 133 authpair = None 134 135 if service_url is None: 136 # Figure out the path to the bitcoin.conf file 137 if btc_conf_file is None: 138 if platform.system() == 'Darwin': 139 btc_conf_file = os.path.expanduser('~/Library/Application Support/Bitcoin/') 140 elif platform.system() == 'Windows': 141 btc_conf_file = os.path.join(os.environ['APPDATA'], 'Bitcoin') 142 else: 143 btc_conf_file = os.path.expanduser('~/.bitcoin') 144 btc_conf_file = os.path.join(btc_conf_file, 'bitcoin.conf') 145 146 # Bitcoin Core accepts empty rpcuser, not specified in btc_conf_file 147 conf = {'rpcuser': ""} 148 149 # Extract contents of bitcoin.conf to build service_url 150 try: 151 with open(btc_conf_file, 'r') as fd: 152 for line in fd.readlines(): 153 if '#' in line: 154 line = line[:line.index('#')] 155 if '=' not in line: 156 continue 157 k, v = line.split('=', 1) 158 conf[k.strip()] = v.strip() 159 160 # Treat a missing bitcoin.conf as though it were empty 161 except FileNotFoundError: 162 pass 163 164 if service_port is None: 165 service_port = bitcoin.params.RPC_PORT 166 conf['rpcport'] = int(conf.get('rpcport', service_port)) 167 conf['rpchost'] = conf.get('rpcconnect', 'localhost') 168 169 service_url = ('%s://%s:%d' % 170 ('http', conf['rpchost'], conf['rpcport'])) 171 172 cookie_dir = conf.get('datadir', os.path.dirname(btc_conf_file)) 173 if bitcoin.params.NAME != "mainnet": 174 cookie_dir = os.path.join(cookie_dir, bitcoin.params.NAME) 175 cookie_file = os.path.join(cookie_dir, ".cookie") 176 try: 177 with open(cookie_file, 'r') as fd: 178 authpair = fd.read() 179 except IOError as err: 180 if 'rpcpassword' in conf: 181 authpair = "%s:%s" % (conf['rpcuser'], conf['rpcpassword']) 182 183 else: 184 raise ValueError('Cookie file unusable (%s) and rpcpassword not specified in the configuration file: %r' % (err, btc_conf_file)) 185 186 else: 187 url = urlparse.urlparse(service_url) 188 authpair = "%s:%s" % (url.username, url.password) 189 190 self.__service_url = service_url 191 self.__url = urlparse.urlparse(service_url) 192 193 if self.__url.scheme not in ('http',): 194 raise ValueError('Unsupported URL scheme %r' % self.__url.scheme) 195 196 if self.__url.port is None: 197 port = httplib.HTTP_PORT 198 else: 199 port = self.__url.port 200 self.__id_count = 0 201 202 if authpair is None: 203 self.__auth_header = None 204 else: 205 authpair = authpair.encode('utf8') 206 self.__auth_header = b"Basic " + base64.b64encode(authpair) 207 208 if connection: 209 self.__conn = connection 210 else: 211 self.__conn = httplib.HTTPConnection(self.__url.hostname, port=port, 212 timeout=timeout) 213 214 def _call(self, service_name, *args): 215 self.__id_count += 1 216 217 postdata = json.dumps({'version': '1.1', 218 'method': service_name, 219 'params': args, 220 'id': self.__id_count}) 221 222 headers = { 223 'Host': self.__url.hostname, 224 'User-Agent': DEFAULT_USER_AGENT, 225 'Content-type': 'application/json', 226 } 227 228 if self.__auth_header is not None: 229 headers['Authorization'] = self.__auth_header 230 231 self.__conn.request('POST', self.__url.path, postdata, headers) 232 233 response = self._get_response() 234 err = response.get('error') 235 if err is not None: 236 if isinstance(err, dict): 237 raise JSONRPCError( 238 {'code': err.get('code', -345), 239 'message': err.get('message', 'error message not specified')}) 240 raise JSONRPCError({'code': -344, 'message': str(err)}) 241 elif 'result' not in response: 242 raise JSONRPCError({ 243 'code': -343, 'message': 'missing JSON-RPC result'}) 244 else: 245 return response['result'] 246 247 def _batch(self, rpc_call_list): 248 postdata = json.dumps(list(rpc_call_list)) 249 250 headers = { 251 'Host': self.__url.hostname, 252 'User-Agent': DEFAULT_USER_AGENT, 253 'Content-type': 'application/json', 254 } 255 256 if self.__auth_header is not None: 257 headers['Authorization'] = self.__auth_header 258 259 self.__conn.request('POST', self.__url.path, postdata, headers) 260 return self._get_response() 261 262 def _get_response(self): 263 http_response = self.__conn.getresponse() 264 if http_response is None: 265 raise JSONRPCError({ 266 'code': -342, 'message': 'missing HTTP response from server'}) 267 268 rdata = http_response.read().decode('utf8') 269 try: 270 return json.loads(rdata, parse_float=decimal.Decimal) 271 except Exception: 272 raise JSONRPCError({ 273 'code': -342, 274 'message': ('non-JSON HTTP response with \'%i %s\' from server: \'%.20s%s\'' 275 % (http_response.status, http_response.reason, 276 rdata, '...' if len(rdata) > 20 else ''))}) 277 278 def close(self): 279 if self.__conn is not None: 280 self.__conn.close() 281 282 def __del__(self): 283 if self.__conn is not None: 284 self.__conn.close() 285 286 287class RawProxy(BaseProxy): 288 """Low-level proxy to a bitcoin JSON-RPC service 289 290 Unlike ``Proxy``, no conversion is done besides parsing JSON. As far as 291 Python is concerned, you can call any method; ``JSONRPCError`` will be 292 raised if the server does not recognize it. 293 """ 294 def __init__(self, 295 service_url=None, 296 service_port=None, 297 btc_conf_file=None, 298 timeout=DEFAULT_HTTP_TIMEOUT, 299 **kwargs): 300 super(RawProxy, self).__init__(service_url=service_url, 301 service_port=service_port, 302 btc_conf_file=btc_conf_file, 303 timeout=timeout, 304 **kwargs) 305 306 def __getattr__(self, name): 307 if name.startswith('__') and name.endswith('__'): 308 # Prevent RPC calls for non-existing python internal attribute 309 # access. If someone tries to get an internal attribute 310 # of RawProxy instance, and the instance does not have this 311 # attribute, we do not want the bogus RPC call to happen. 312 raise AttributeError 313 314 # Create a callable to do the actual call 315 f = lambda *args: self._call(name, *args) 316 317 # Make debuggers show <function bitcoin.rpc.name> rather than <function 318 # bitcoin.rpc.<lambda>> 319 f.__name__ = name 320 return f 321 322 323class Proxy(BaseProxy): 324 """Proxy to a bitcoin RPC service 325 326 Unlike ``RawProxy``, data is passed as ``bitcoin.core`` objects or packed 327 bytes, rather than JSON or hex strings. Not all methods are implemented 328 yet; you can use ``call`` to access missing ones in a forward-compatible 329 way. Assumes Bitcoin Core version >= v0.16.0; older versions mostly work, 330 but there are a few incompatibilities. 331 """ 332 333 def __init__(self, 334 service_url=None, 335 service_port=None, 336 btc_conf_file=None, 337 timeout=DEFAULT_HTTP_TIMEOUT, 338 **kwargs): 339 """Create a proxy object 340 341 If ``service_url`` is not specified, the username and password are read 342 out of the file ``btc_conf_file``. If ``btc_conf_file`` is not 343 specified, ``~/.bitcoin/bitcoin.conf`` or equivalent is used by 344 default. The default port is set according to the chain parameters in 345 use: mainnet, testnet, or regtest. 346 347 Usually no arguments to ``Proxy()`` are needed; the local bitcoind will 348 be used. 349 350 ``timeout`` - timeout in seconds before the HTTP interface times out 351 """ 352 353 super(Proxy, self).__init__(service_url=service_url, 354 service_port=service_port, 355 btc_conf_file=btc_conf_file, 356 timeout=timeout, 357 **kwargs) 358 359 def call(self, service_name, *args): 360 """Call an RPC method by name and raw (JSON encodable) arguments""" 361 return self._call(service_name, *args) 362 363 def dumpprivkey(self, addr): 364 """Return the private key matching an address 365 """ 366 r = self._call('dumpprivkey', str(addr)) 367 368 return CBitcoinSecret(r) 369 370 def fundrawtransaction(self, tx, include_watching=False): 371 """Add inputs to a transaction until it has enough in value to meet its out value. 372 373 include_watching - Also select inputs which are watch only 374 375 Returns dict: 376 377 {'tx': Resulting tx, 378 'fee': Fee the resulting transaction pays, 379 'changepos': Position of added change output, or -1, 380 } 381 """ 382 hextx = hexlify(tx.serialize()) 383 r = self._call('fundrawtransaction', hextx, include_watching) 384 385 r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) 386 del r['hex'] 387 388 r['fee'] = int(r['fee'] * COIN) 389 390 return r 391 392 def generate(self, numblocks): 393 """ 394 DEPRECATED (will be removed in bitcoin-core v0.19) 395 396 Mine blocks immediately (before the RPC call returns) 397 398 numblocks - How many blocks are generated immediately. 399 400 Returns iterable of block hashes generated. 401 """ 402 r = self._call('generate', numblocks) 403 return (lx(blk_hash) for blk_hash in r) 404 405 def generatetoaddress(self, numblocks, addr): 406 """Mine blocks immediately (before the RPC call returns) and 407 allocate block reward to passed address. Replaces deprecated 408 "generate(self,numblocks)" method. 409 410 numblocks - How many blocks are generated immediately. 411 addr - Address to receive block reward (CBitcoinAddress instance) 412 413 Returns iterable of block hashes generated. 414 """ 415 r = self._call('generatetoaddress', numblocks, str(addr)) 416 return (lx(blk_hash) for blk_hash in r) 417 418 def getaccountaddress(self, account=None): 419 """Return the current Bitcoin address for receiving payments to this 420 account.""" 421 r = self._call('getaccountaddress', account) 422 return CBitcoinAddress(r) 423 424 def getbalance(self, account='*', minconf=1, include_watchonly=False): 425 """Get the balance 426 427 account - The selected account. Defaults to "*" for entire wallet. It 428 may be the default account using "". 429 430 minconf - Only include transactions confirmed at least this many times. 431 (default=1) 432 433 include_watchonly - Also include balance in watch-only addresses (see 'importaddress') 434 (default=False) 435 """ 436 r = self._call('getbalance', account, minconf, include_watchonly) 437 return int(r*COIN) 438 439 def getbestblockhash(self): 440 """Return hash of best (tip) block in longest block chain.""" 441 return lx(self._call('getbestblockhash')) 442 443 def getblockheader(self, block_hash, verbose=False): 444 """Get block header <block_hash> 445 446 verbose - If true a dict is returned with the values returned by 447 getblockheader that are not in the block header itself 448 (height, nextblockhash, etc.) 449 450 Raises IndexError if block_hash is not valid. 451 """ 452 try: 453 block_hash = b2lx(block_hash) 454 except TypeError: 455 raise TypeError('%s.getblockheader(): block_hash must be bytes; got %r instance' % 456 (self.__class__.__name__, block_hash.__class__)) 457 try: 458 r = self._call('getblockheader', block_hash, verbose) 459 except InvalidAddressOrKeyError as ex: 460 raise IndexError('%s.getblockheader(): %s (%d)' % 461 (self.__class__.__name__, ex.error['message'], ex.error['code'])) 462 463 if verbose: 464 nextblockhash = None 465 if 'nextblockhash' in r: 466 nextblockhash = lx(r['nextblockhash']) 467 return {'confirmations':r['confirmations'], 468 'height':r['height'], 469 'mediantime':r['mediantime'], 470 'nextblockhash':nextblockhash, 471 'chainwork':x(r['chainwork'])} 472 else: 473 return CBlockHeader.deserialize(unhexlify(r)) 474 475 476 def getblock(self, block_hash): 477 """Get block <block_hash> 478 479 Raises IndexError if block_hash is not valid. 480 """ 481 try: 482 block_hash = b2lx(block_hash) 483 except TypeError: 484 raise TypeError('%s.getblock(): block_hash must be bytes; got %r instance' % 485 (self.__class__.__name__, block_hash.__class__)) 486 try: 487 # With this change ( https://github.com/bitcoin/bitcoin/commit/96c850c20913b191cff9f66fedbb68812b1a41ea#diff-a0c8f511d90e83aa9b5857e819ced344 ), 488 # bitcoin core's rpc takes 0/1/2 instead of true/false as the 2nd argument which specifies verbosity, since v0.15.0. 489 # The change above is backward-compatible so far; the old "false" is taken as the new "0". 490 r = self._call('getblock', block_hash, False) 491 except InvalidAddressOrKeyError as ex: 492 raise IndexError('%s.getblock(): %s (%d)' % 493 (self.__class__.__name__, ex.error['message'], ex.error['code'])) 494 return CBlock.deserialize(unhexlify(r)) 495 496 def getblockcount(self): 497 """Return the number of blocks in the longest block chain""" 498 return self._call('getblockcount') 499 500 def getblockhash(self, height): 501 """Return hash of block in best-block-chain at height. 502 503 Raises IndexError if height is not valid. 504 """ 505 try: 506 return lx(self._call('getblockhash', height)) 507 except InvalidParameterError as ex: 508 raise IndexError('%s.getblockhash(): %s (%d)' % 509 (self.__class__.__name__, ex.error['message'], ex.error['code'])) 510 511 def getinfo(self): 512 """Return a JSON object containing various state info""" 513 r = self._call('getinfo') 514 if 'balance' in r: 515 r['balance'] = int(r['balance'] * COIN) 516 if 'paytxfee' in r: 517 r['paytxfee'] = int(r['paytxfee'] * COIN) 518 return r 519 520 def getmininginfo(self): 521 """Return a JSON object containing mining-related information""" 522 return self._call('getmininginfo') 523 524 def getnewaddress(self, account=None): 525 """Return a new Bitcoin address for receiving payments. 526 527 If account is not None, it is added to the address book so payments 528 received with the address will be credited to account. 529 """ 530 r = None 531 if account is not None: 532 r = self._call('getnewaddress', account) 533 else: 534 r = self._call('getnewaddress') 535 536 return CBitcoinAddress(r) 537 538 def getrawchangeaddress(self): 539 """Returns a new Bitcoin address, for receiving change. 540 541 This is for use with raw transactions, NOT normal use. 542 """ 543 r = self._call('getrawchangeaddress') 544 return CBitcoinAddress(r) 545 546 def getrawmempool(self, verbose=False): 547 """Return the mempool""" 548 if verbose: 549 return self._call('getrawmempool', verbose) 550 551 else: 552 r = self._call('getrawmempool') 553 r = [lx(txid) for txid in r] 554 return r 555 556 def getrawtransaction(self, txid, verbose=False): 557 """Return transaction with hash txid 558 559 Raises IndexError if transaction not found. 560 561 verbose - If true a dict is returned instead with additional 562 information on the transaction. 563 564 Note that if all txouts are spent and the transaction index is not 565 enabled the transaction may not be available. 566 """ 567 try: 568 r = self._call('getrawtransaction', b2lx(txid), 1 if verbose else 0) 569 except InvalidAddressOrKeyError as ex: 570 raise IndexError('%s.getrawtransaction(): %s (%d)' % 571 (self.__class__.__name__, ex.error['message'], ex.error['code'])) 572 if verbose: 573 r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) 574 del r['hex'] 575 del r['txid'] 576 del r['version'] 577 del r['locktime'] 578 del r['vin'] 579 del r['vout'] 580 r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None 581 else: 582 r = CTransaction.deserialize(unhexlify(r)) 583 584 return r 585 586 def getreceivedbyaddress(self, addr, minconf=1): 587 """Return total amount received by given a (wallet) address 588 589 Get the amount received by <address> in transactions with at least 590 [minconf] confirmations. 591 592 Works only for addresses in the local wallet; other addresses will 593 always show zero. 594 595 addr - The address. (CBitcoinAddress instance) 596 597 minconf - Only include transactions confirmed at least this many times. 598 (default=1) 599 """ 600 r = self._call('getreceivedbyaddress', str(addr), minconf) 601 return int(r * COIN) 602 603 def gettransaction(self, txid): 604 """Get detailed information about in-wallet transaction txid 605 606 Raises IndexError if transaction not found in the wallet. 607 608 FIXME: Returned data types are not yet converted. 609 """ 610 try: 611 r = self._call('gettransaction', b2lx(txid)) 612 except InvalidAddressOrKeyError as ex: 613 raise IndexError('%s.getrawtransaction(): %s (%d)' % 614 (self.__class__.__name__, ex.error['message'], ex.error['code'])) 615 return r 616 617 def gettxout(self, outpoint, includemempool=True): 618 """Return details about an unspent transaction output. 619 620 Raises IndexError if outpoint is not found or was spent. 621 622 includemempool - Include mempool txouts 623 """ 624 r = self._call('gettxout', b2lx(outpoint.hash), outpoint.n, includemempool) 625 626 if r is None: 627 raise IndexError('%s.gettxout(): unspent txout %r not found' % (self.__class__.__name__, outpoint)) 628 629 r['txout'] = CTxOut(int(r['value'] * COIN), 630 CScript(unhexlify(r['scriptPubKey']['hex']))) 631 del r['value'] 632 del r['scriptPubKey'] 633 r['bestblock'] = lx(r['bestblock']) 634 return r 635 636 def importaddress(self, addr, label='', rescan=True): 637 """Adds an address or pubkey to wallet without the associated privkey.""" 638 addr = str(addr) 639 640 r = self._call('importaddress', addr, label, rescan) 641 return r 642 643 def listunspent(self, minconf=0, maxconf=9999999, addrs=None): 644 """Return unspent transaction outputs in wallet 645 646 Outputs will have between minconf and maxconf (inclusive) 647 confirmations, optionally filtered to only include txouts paid to 648 addresses in addrs. 649 """ 650 r = None 651 if addrs is None: 652 r = self._call('listunspent', minconf, maxconf) 653 else: 654 addrs = [str(addr) for addr in addrs] 655 r = self._call('listunspent', minconf, maxconf, addrs) 656 657 r2 = [] 658 for unspent in r: 659 unspent['outpoint'] = COutPoint(lx(unspent['txid']), unspent['vout']) 660 del unspent['txid'] 661 del unspent['vout'] 662 663 # address isn't always available as Bitcoin Core allows scripts w/o 664 # an address type to be imported into the wallet, e.g. non-p2sh 665 # segwit 666 try: 667 unspent['address'] = CBitcoinAddress(unspent['address']) 668 except KeyError: 669 pass 670 unspent['scriptPubKey'] = CScript(unhexlify(unspent['scriptPubKey'])) 671 unspent['amount'] = int(unspent['amount'] * COIN) 672 r2.append(unspent) 673 return r2 674 675 def lockunspent(self, unlock, outpoints): 676 """Lock or unlock outpoints""" 677 json_outpoints = [{'txid':b2lx(outpoint.hash), 'vout':outpoint.n} 678 for outpoint in outpoints] 679 return self._call('lockunspent', unlock, json_outpoints) 680 681 def sendrawtransaction(self, tx, allowhighfees=False): 682 """Submit transaction to local node and network. 683 684 allowhighfees - Allow even if fees are unreasonably high. 685 """ 686 hextx = hexlify(tx.serialize()) 687 r = None 688 if allowhighfees: 689 r = self._call('sendrawtransaction', hextx, True) 690 else: 691 r = self._call('sendrawtransaction', hextx) 692 return lx(r) 693 694 def sendmany(self, fromaccount, payments, minconf=1, comment='', subtractfeefromamount=[]): 695 """Send amount to given addresses. 696 697 payments - dict with {address: amount} 698 """ 699 json_payments = {str(addr):float(amount)/COIN 700 for addr, amount in payments.items()} 701 r = self._call('sendmany', fromaccount, json_payments, minconf, comment, subtractfeefromamount) 702 return lx(r) 703 704 def sendtoaddress(self, addr, amount, comment='', commentto='', subtractfeefromamount=False): 705 """Send amount to a given address""" 706 addr = str(addr) 707 amount = float(amount)/COIN 708 r = self._call('sendtoaddress', addr, amount, comment, commentto, subtractfeefromamount) 709 return lx(r) 710 711 def signrawtransaction(self, tx, *args): 712 """Sign inputs for transaction 713 714 FIXME: implement options 715 """ 716 hextx = hexlify(tx.serialize()) 717 r = self._call('signrawtransaction', hextx, *args) 718 r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) 719 del r['hex'] 720 return r 721 722 def signrawtransactionwithwallet(self, tx, *args): 723 """Sign inputs for transaction 724 bicoincore >= 0.17.x 725 726 FIXME: implement options 727 """ 728 hextx = hexlify(tx.serialize()) 729 r = self._call('signrawtransactionwithwallet', hextx, *args) 730 r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) 731 del r['hex'] 732 return r 733 734 def submitblock(self, block, params=None): 735 """Submit a new block to the network. 736 737 params is optional and is currently ignored by bitcoind. See 738 https://en.bitcoin.it/wiki/BIP_0022 for full specification. 739 """ 740 hexblock = hexlify(block.serialize()) 741 if params is not None: 742 return self._call('submitblock', hexblock, params) 743 else: 744 return self._call('submitblock', hexblock) 745 746 def validateaddress(self, address): 747 """Return information about an address""" 748 r = self._call('validateaddress', str(address)) 749 if r['isvalid']: 750 r['address'] = CBitcoinAddress(r['address']) 751 if 'pubkey' in r: 752 r['pubkey'] = unhexlify(r['pubkey']) 753 return r 754 755 def unlockwallet(self, password, timeout=60): 756 """Stores the wallet decryption key in memory for 'timeout' seconds. 757 758 password - The wallet passphrase. 759 760 timeout - The time to keep the decryption key in seconds. 761 (default=60) 762 """ 763 r = self._call('walletpassphrase', password, timeout) 764 return r 765 766 def _addnode(self, node, arg): 767 r = self._call('addnode', node, arg) 768 return r 769 770 def addnode(self, node): 771 return self._addnode(node, 'add') 772 773 def addnodeonetry(self, node): 774 return self._addnode(node, 'onetry') 775 776 def removenode(self, node): 777 return self._addnode(node, 'remove') 778 779__all__ = ( 780 'JSONRPCError', 781 'ForbiddenBySafeModeError', 782 'InvalidAddressOrKeyError', 783 'InvalidParameterError', 784 'VerifyError', 785 'VerifyRejectedError', 786 'VerifyAlreadyInChainError', 787 'InWarmupError', 788 'RawProxy', 789 'Proxy', 790) 791