Esempio n. 1
0
 def address_to_hashX(self, address):
     if isinstance(address, str):
         try:
             return self.coin.address_to_hashX(address)
         except Exception:
             pass
     raise RPCError('{} is not a valid address'.format(address))
Esempio n. 2
0
 def new_subscription(self):
     if self.subs_room <= 0:
         self.subs_room = self.max_subs - self.sub_count()
         if self.subs_room <= 0:
             raise RPCError('server subscription limit {:,d} reached'
                            .format(self.max_subs))
     self.subs_room -= 1
Esempio n. 3
0
    async def tx_merkle(self, tx_hash, height):
        '''tx_hash is a hex string.'''
        hex_hashes = await self.daemon_request('block_hex_hashes', height, 1)
        block = await self.daemon_request('deserialised_block', hex_hashes[0])
        tx_hashes = block['tx']
        try:
            pos = tx_hashes.index(tx_hash)
        except ValueError:
            raise RPCError('tx hash {} not in block {} at height {:,d}'.format(
                tx_hash, hex_hashes[0], height))

        idx = pos
        hashes = [hex_str_to_hash(txh) for txh in tx_hashes]
        merkle_branch = []
        while len(hashes) > 1:
            if len(hashes) & 1:
                hashes.append(hashes[-1])
            idx = idx - 1 if (idx & 1) else idx + 1
            merkle_branch.append(hash_to_str(hashes[idx]))
            idx //= 2
            hashes = [
                double_sha256(hashes[n] + hashes[n + 1])
                for n in range(0, len(hashes), 2)
            ]

        return {"block_height": height, "merkle": merkle_branch, "pos": pos}
Esempio n. 4
0
    def server_version(self, client_name=None, protocol_version=None):
        '''Returns the server version as a string.

        client_name: a string identifying the client
        protocol_version: the protocol version spoken by the client
        '''
        if client_name:
            self.client = str(client_name)[:17]
            try:
                self.client_version = tuple(
                    int(part) for part in self.client.split('.'))
            except Exception:
                pass

        # Find the highest common protocol version.  Disconnect if
        # that protocol version in unsupported.
        ptuple = util.protocol_version(protocol_version, version.PROTOCOL_MIN,
                                       version.PROTOCOL_MAX)

        # From protocol version 1.1, protocol_version cannot be omitted
        if ptuple is None or (ptuple >= (1, 1) and protocol_version is None):
            self.log_info('unsupported protocol version request {}'.format(
                protocol_version))
            raise RPCError(
                'unsupported protocol version: {}'.format(protocol_version),
                JSONRPC.FATAL_ERROR)

        self.set_protocol_handlers(ptuple)

        # The return value depends on the protocol version
        if ptuple < (1, 1):
            return version.VERSION
        else:
            return (version.VERSION, self.protocol_version)
Esempio n. 5
0
 def scripthash_to_hashX(self, scripthash):
     try:
         bin_hash = hex_str_to_hash(scripthash)
         if len(bin_hash) == 32:
             return bin_hash[:self.coin.HASHX_LEN]
     except Exception:
         pass
     raise RPCError('{} is not a valid script hash'.format(scripthash))
Esempio n. 6
0
 def rpc_daemon_url(self, daemon_url=None):
     '''Replace the daemon URL.'''
     daemon_url = daemon_url or self.env.daemon_url
     try:
         self.daemon.set_urls(self.env.coin.daemon_urls(daemon_url))
     except Exception as e:
         raise RPCError('an error occured: {}'.format(e))
     return 'now using daemon at {}'.format(self.daemon.logged_url())
Esempio n. 7
0
    def rpc_reorg(self, count=3):
        '''Force a reorg of the given number of blocks.

        count: number of blocks to reorg (default 3)
        '''
        count = self.non_negative_integer(count)
        if not self.bp.force_chain_reorg(count):
            raise RPCError('still catching up with daemon')
        return 'scheduled a reorg of {:,d} blocks'.format(count)
Esempio n. 8
0
 def assert_tx_hash(self, value):
     '''Raise an RPCError if the value is not a valid transaction
     hash.'''
     try:
         if len(util.hex_to_bytes(value)) == 32:
             return
     except Exception:
         pass
     raise RPCError('{} should be a transaction hash'.format(value))
Esempio n. 9
0
 async def hash160_contract_subscribe(self, hash160, contract_addr):
     if len(self.contract_subs) >= self.max_subs:
         raise RPCError(
             'your contract subscription limit {:,d} reached'.format(
                 self.max_subs))
     hashY = self.controller.coin.hash160_contract_to_hashY(
         hash160, contract_addr)
     self.contract_subs[hashY] = (hash160, contract_addr)
     return await self.hash160_contract_status(hash160, contract_addr)
Esempio n. 10
0
    async def hashX_subscribe(self, hashX, alias):
        # First check our limit.
        if len(self.hashX_subs) >= self.max_subs:
            raise RPCError('your address subscription limit {:,d} reached'
                           .format(self.max_subs))

        # Now let the controller check its limit
        self.controller.new_subscription()
        self.hashX_subs[hashX] = alias
        return await self.address_status(hashX)
Esempio n. 11
0
 def electrum_header(self, height):
     '''Return the binary header at the given height.'''
     if not 0 <= height <= self.bp.db_height:
         raise RPCError('height {:,d} out of range'.format(height))
     if height in self.header_cache:
         return self.header_cache[height]
     header = self.bp.read_headers(height, 1)
     header = self.coin.electrum_header(header, height)
     self.header_cache[height] = header
     return header
Esempio n. 12
0
 def non_negative_integer(self, value):
     '''Return param value it is or can be converted to a non-negative
     integer, otherwise raise an RPCError.'''
     try:
         value = int(value)
         if value >= 0:
             return value
     except ValueError:
         pass
     raise RPCError('{} should be a non-negative integer'.format(value))
Esempio n. 13
0
 def to_tx_hash(self, value):
     '''Raise an RPCError if the value is not a valid transaction
     hash.'''
     if isinstance(value, str) and len(value) == 64:
         try:
             bytes.fromhex(value)
             return value
         except ValueError:
             pass
     raise RPCError('{} should be a transaction hash'.format(value))
Esempio n. 14
0
 async def new_subscription(self, address):
     if self.subs_room <= 0:
         self.subs_room = self.max_subs - self.sub_count()
         if self.subs_room <= 0:
             raise RPCError(
                 'server subscription limit {:,d} reached'.format(
                     self.max_subs))
     self.subs_room -= 1
     hashX = self.address_to_hashX(address)
     status = await self.address_status(hashX)
     return hashX, status
Esempio n. 15
0
 async def masternode_announce_broadcast(self, signmnb):
     '''Pass through the masternode announce message to be broadcast
     by the daemon.'''
     try:
         return await self.daemon.masternode_broadcast(['relay', signmnb])
     except DaemonError as e:
         error, = e.args
         message = error['message']
         self.log_info('masternode_broadcast: {}'.format(message))
         raise RPCError('the masternode broadcast was rejected.'
                        '\n\n{}\n[{}]'.format(message, signmnb))
Esempio n. 16
0
    def for_each_session(self, session_ids, operation):
        if not isinstance(session_ids, list):
            raise RPCError('expected a list of session IDs')

        result = []
        for session_id in session_ids:
            session = self.lookup_session(session_id)
            if session:
                result.append(operation(session))
            else:
                result.append('unknown session: {}'.format(session_id))
        return result
Esempio n. 17
0
    async def listvoterbills(self, addr):
        '''Get bill list that addr voted.

        addr: the address'''
        # This returns errors as JSON RPC errors, as is natural
        try:
            return await self.daemon.listvoterbills([addr])
        except DaemonError as e:
            error, = e.args
            message = error['message']
            self.log_info('listvoterbills: {}'.format(message), throttle=True)
            raise RPCError('the name was rejected by network rules.'
                           '\n\n{}\n[{}]'.format(message, addr))
Esempio n. 18
0
    async def createwitness(self, address):
        '''Create a witness on the blockchain.

        address: the address with witness number attached'''
        # This returns errors as JSON RPC errors, as is natural
        try:
            return await self.daemon.getnetworkinfo()
        except DaemonError as e:
            error, = e.args
            message = error['message']
            self.log_info('createwitness: {}'.format(message), throttle=True)
            raise RPCError('the address was rejected by network rules.'
                           '\n\n{}\n[{}]'.format(message, address))
Esempio n. 19
0
    async def address_subscribe(self, address):
        '''Subscribe to an address.

        address: the address to subscribe to'''
        # First check our limit.
        if len(self.hashX_subs) >= self.max_subs:
            raise RPCError(
                'your address subscription limit {:,d} reached'.format(
                    self.max_subs))
        # Now let the controller check its limit
        hashX, status = await self.controller.new_subscription(address)
        self.hashX_subs[hashX] = address
        return status
Esempio n. 20
0
    async def listcommitteevoters(self, name):
        '''Get who voted to committee name.

        name: the name of one committee'''
        # This returns errors as JSON RPC errors, as is natural
        try:
            return await self.daemon.listcommitteevoters([name])
        except DaemonError as e:
            error, = e.args
            message = error['message']
            self.log_info('listcommitteevoters: {}'.format(message),
                          throttle=True)
            raise RPCError('the name was rejected by network rules.'
                           '\n\n{}\n[{}]'.format(message, name))
Esempio n. 21
0
    def block_get_chunk(self, index):
        '''Return a chunk of block headers as a hexadecimal string.

        index: the chunk index'''
        index = self.controller.non_negative_integer(index)
        self.chunk_indices.append(index)
        self.chunk_indices = self.chunk_indices[-5:]
        # -2 allows backing up a single chunk but no more.
        if index <= max(self.chunk_indices[:-2], default=-1):
            msg = ('chunk indices not advancing (wrong network?): {}'.format(
                self.chunk_indices))
            # INVALID_REQUEST triggers a disconnect
            raise RPCError(msg, JSONRPC.INVALID_REQUEST)
        return self.controller.get_chunk(index)
Esempio n. 22
0
    async def listvoteddelegates(self, address):
        '''Get vote inof according address.

        address: the address with vote info attached'''
        # This returns errors as JSON RPC errors, as is natural
        try:
            return await self.daemon.listvoteddelegates([address])
        except DaemonError as e:
            error, = e.args
            message = error['message']
            self.log_info('listvoteddelegates: {}'.format(message),
                          throttle=True)
            raise RPCError('the address was rejected by network rules.'
                           '\n\n{}\n[{}]'.format(message, address))
Esempio n. 23
0
    async def listreceivedvotes(self, name):
        '''Get voted inof according name.

        name: the name of one delegate'''
        # This returns errors as JSON RPC errors, as is natural
        try:
            return await self.daemon.listreceivedvotes([name])
        except DaemonError as e:
            error, = e.args
            message = error['message']
            self.log_info('listreceivedvotes: {}'.format(message),
                          throttle=True)
            raise RPCError('the name was rejected by network rules.'
                           '\n\n{}\n[{}]'.format(message, name))
Esempio n. 24
0
    async def transaction_broadcast(self, raw_tx):
        '''Broadcast a raw transaction to the network.

        raw_tx: the raw transaction as a hexadecimal string'''
        # This returns errors as JSON RPC errors, as is natural
        try:
            tx_hash = await self.daemon.sendrawtransaction([raw_tx])
            self.txs_sent += 1
            self.log_info('sent tx: {}'.format(tx_hash))
            self.controller.sent_tx(tx_hash)
            return tx_hash
        except DaemonError as e:
            error, = e.args
            message = error['message']
            self.log_info('sendrawtransaction: {}'.format(message),
                          throttle=True)
            raise RPCError('the transaction was rejected by network rules.'
                           '\n\n{}\n[{}]'.format(message, raw_tx))
Esempio n. 25
0
    async def address_get_proof(self, address):
        '''Return the UTXO proof of an address.'''
        hashX = self.address_to_hashX(address)
        raise RPCError('address.get_proof is not yet implemented')

        async def address_listunspent(self, address):
        '''Return the list of UTXOs of an address.'''
        hashX = self.address_to_hashX(address)
        script = self.pay_to_address_script(address)
        scriptKey = script.hex()
        return [{'tx_hash': hash_to_str(utxo.tx_hash), 'tx_pos': utxo.tx_pos,
                 'height': utxo.height, 'value': utxo.value, 'script': scriptKey}
                for utxo in sorted(await self.get_utxos(hashX))]

    async def scripthash_listunspent(self, scripthash):
        '''Return the list of UTXOs of a scripthash.'''
        hashX = self.scripthash_to_hashX(scripthash)
        return [{'tx_hash': hash_to_str(utxo.tx_hash), 'tx_pos': utxo.tx_pos,
                 'height': utxo.height, 'value': utxo.value}
                for utxo in sorted(await self.get_utxos(hashX))]

    def block_get_header(self, height):
        '''The deserialized header at a given height.

        height: the header's height'''
        height = self.non_negative_integer(height)
        return self.electrum_header(height)

    async def estimatefee(self, number):
        '''The estimated transaction fee per kilobyte to be paid for a
        transaction to be included within a certain number of blocks.

        number: the number of blocks
        '''
        number = self.non_negative_integer(number)
        return await self.daemon_request('estimatefee', [number])

    async def relayfee(self):
        '''The minimum fee a low-priority tx must pay in order to be accepted
        to the daemon's memory pool.'''
        return await self.daemon_request('relayfee')

    async def transaction_get(self, tx_hash):
        '''Return the serialized raw transaction given its hash

        tx_hash: the transaction hash as a hexadecimal string
        '''
        self.assert_tx_hash(tx_hash)
        return await self.daemon_request('getrawtransaction', tx_hash)

    async def transaction_get_1_0(self, tx_hash, height=None):
        '''Return the serialized raw transaction given its hash

        tx_hash: the transaction hash as a hexadecimal string
        height: ignored, do not use
        '''
        # For some reason Electrum protocol 1.0 passes a height.
        return await self.transaction_get(tx_hash)

    async def transaction_get_merkle(self, tx_hash, height):
        '''Return the markle tree to a confirmed transaction given its hash
        and height.

        tx_hash: the transaction hash as a hexadecimal string
        height: the height of the block it is in
        '''
        self.assert_tx_hash(tx_hash)
        height = self.non_negative_integer(height)
        return await self.tx_merkle(tx_hash, height)

    async def utxo_get_address(self, tx_hash, index):
        '''Returns the address sent to in a UTXO, or null if the UTXO
        cannot be found.

        tx_hash: the transaction hash of the UTXO
        index: the index of the UTXO in the transaction'''
        # Used only for electrum client command-line requests.  We no
        # longer index by address, so need to request the raw
        # transaction.  So it works for any TXO not just UTXOs.
        self.assert_tx_hash(tx_hash)
        index = self.non_negative_integer(index)
        raw_tx = await self.daemon_request('getrawtransaction', tx_hash)
        if not raw_tx:
            return None
        raw_tx = util.hex_to_bytes(raw_tx)
        tx, tx_hash = self.coin.DESERIALIZER(raw_tx).read_tx()
        if index >= len(tx.outputs):
            return None
        return self.coin.address_from_script(tx.outputs[index].pk_script)

    # Signal, exception handlers.

    def on_signal(self, signame):
        '''Call on receipt of a signal to cleanly shutdown.'''
        self.logger.warning('received {} signal, initiating shutdown'
                            .format(signame))
        self.initiate_shutdown()

    def on_exception(self, loop, context):
        '''Suppress spurious messages it appears we cannot control.'''
        message = context.get('message')
        if message not in self.SUPPRESS_MESSAGES:
            if not ('task' in context and
                    'accept_connection2()' in repr(context.get('task'))):
                loop.default_exception_handler(context)

    def run(self):
        # Install signal handlers and exception handler
        loop = self.loop
        for signame in ('SIGINT', 'SIGTERM'):
            loop.add_signal_handler(getattr(signal, signame),
                                    partial(self.on_signal, signame))
        loop.set_exception_handler(self.on_exception)

        # Run the main loop to completion
        future = asyncio.ensure_future(self.main_loop())
        loop.run_until_complete(future)
        loop.close()
Esempio n. 26
0
 def assert_boolean(self, value):
     '''Return param value it is boolean otherwise raise an RPCError.'''
     if value in (False, True):
         return value
     raise RPCError('{} should be a boolean value'.format(value))
Esempio n. 27
0
 async def daemon_request(self, method, *args):
     '''Catch a DaemonError and convert it to an RPCError.'''
     try:
         return await getattr(self.daemon, method)(*args)
     except DaemonError as e:
         raise RPCError('daemon error: {}'.format(e))
Esempio n. 28
0
 def pay_to_address_script(self, address):
     try:
         return self.coin.pay_to_address_script(address)
     except Exception:
         pass
     raise RPCError('{} is not a valid address'.format(address))
Esempio n. 29
0
 def raw_header(self, height):
     '''Return the binary header at the given height.'''
     header, n = self.bp.read_headers(height, 1)
     if n != 1:
         raise RPCError('height {:,d} out of range'.format(height))
     return header
Esempio n. 30
0
 async def address_get_proof(self, address):
     '''Return the UTXO proof of an address.'''
     hashX = self.address_to_hashX(address)
     raise RPCError('address.get_proof is not yet implemented')