Esempio n. 1
0
    async def blockchain_scripthash_get_history(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.get_history
        Return the confirmed and unconfirmed history of a script hash.
        """
        if "params" not in query or len(query["params"]) != 1:
            return JsonRPCError.invalidparams()

        if not is_hash256_str(query["params"][0]):
            return JsonRPCError.invalidparams()

        _ec, data = await self.bx.fetch_history4(query["params"][0])
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        self.log.debug("hist: %s", data)
        ret = []
        # TODO: mempool
        for i in data:
            if "received" in i:
                ret.append({
                    "height": i["received"]["height"],
                    "tx_hash": hash_to_hex_str(i["received"]["hash"]),
                })
            if "spent" in i:
                ret.append({
                    "height": i["spent"]["height"],
                    "tx_hash": hash_to_hex_str(i["spent"]["hash"]),
                })

        return {"result": ret}
Esempio n. 2
0
 def __scripthash_status_from_history(history):
     status = []
     for i in history:
         if "received" in i:
             status.append((
                 hash_to_hex_str(i["received"]["hash"]),
                 i["received"]["height"],
             ))
         if "spent" in i:
             status.append((
                 hash_to_hex_str(i["spent"]["hash"]),
                 i["spent"]["height"],
             ))
     return status
Esempio n. 3
0
    async def blockchain_scripthash_listunspent(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.listunspent
        Return an ordered list of UTXOs sent to a script hash.
        """
        if "params" not in query or len(query["params"]) != 1:
            return JsonRPCError.invalidparams()

        scripthash = query["params"][0]
        if not is_hash256_str(scripthash):
            return JsonRPCError.invalidparams()

        _ec, utxo = await self.bx.fetch_utxo(scripthash)
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        # TODO: Check mempool
        ret = []
        for i in utxo:
            rec = i["received"]
            ret.append({
                "tx_pos": rec["index"],
                "value": i["value"],
                "tx_hash": hash_to_hex_str(rec["hash"]),
                "height": rec["height"],
            })

        if len(ret) >= 2:
            ret.reverse()
        return {"result": ret}
Esempio n. 4
0
    async def scripthash_listunspent(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.listunspent
        Return an ordered list of UTXOs sent to a script hash.
        """
        if "params" not in query or len(query["params"]) != 1:
            return JsonRPCError.invalidparams()

        scripthash = query["params"][0]
        if not is_hash256_str(scripthash):
            return JsonRPCError.invalidparams()

        _ec, utxo = await self.bx.fetch_utxo(scripthash)
        if _ec and _ec != ZMQError.success:
            self.log.error("bx.fetch_utxo: %s", _ec.name)
            return JsonRPCError.internalerror()

        ret = []
        for i in utxo:
            rec = i["received"]
            ret.append({
                "tx_pos":
                rec["index"],
                "value":
                i["value"],
                "tx_hash":
                hash_to_hex_str(rec["hash"]),
                "height":
                rec["height"] if rec["height"] != 4294967295 else 0,
            })

        return {"result": ret}
Esempio n. 5
0
 def make_tuple(row):
     kind, height, tx_hash, index, value = row
     return (
         kind,
         {
             "hash": tx_hash,
             "index": index
         },
         height,
         value,
         checksum(hash_to_hex_str(tx_hash), index),
     )
Esempio n. 6
0
    async def _merkle_proof_for_headers(self, height, idx):
        """Extremely inefficient merkle proof for headers"""
        # The following works, but is extremely inefficient.
        # The best solution would be to figure something out in
        # libbitcoin-server
        cp_headers = []

        for i in range(0, height + 1):
            _ec, data = await self.bx.fetch_block_header(i)
            if _ec and _ec != ZMQError.success:
                self.log.error("bx.fetch_block_header: %s", _ec.name)
                return JsonRPCError.internalerror()
            cp_headers.append(data)

        branch, root = merkle_branch_and_root(
            [double_sha256(i) for i in cp_headers], idx)

        return {
            "branch": [hash_to_hex_str(i) for i in branch],
            "header": safe_hexlify(cp_headers[idx]),
            "root": hash_to_hex_str(root),
        }
Esempio n. 7
0
    async def blockchain_scripthash_subscribe(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.subscribe
        Subscribe to a script hash.
        """
        if "params" not in query or len(query["params"]) != 1:
            return JsonRPCError.invalidparams()

        scripthash = query["params"][0]
        if not is_hash256_str(scripthash):
            return JsonRPCError.invalidparams()

        _ec, history = await self.bx.fetch_history4(scripthash)
        if _ec and _ec != 0:
            return JsonRPCError.internalerror()

        task = asyncio.create_task(self.scripthash_notifier(writer, scripthash))
        self.sh_subscriptions[scripthash] = {"task": task}

        if len(history) < 1:
            return {"result": None}

        # TODO: Check how history4 acts for mempool/unconfirmed
        status = []
        for i in history:
            if "received" in i:
                status.append((
                    hash_to_hex_str(i["received"]["hash"]),
                    i["received"]["height"],
                ))
            if "spent" in i:
                status.append((
                    hash_to_hex_str(i["spent"]["hash"]),
                    i["spent"]["height"],
                ))

        self.sh_subscriptions[scripthash]["status"] = status
        return {"result": ElectrumProtocol.__scripthash_status(status)}
Esempio n. 8
0
    async def scripthash_notifier(self, writer, scripthash):
        # TODO: Mempool
        # TODO: This is still flaky and not always notified. Investigate.
        self.log.debug("notifier")
        method = "blockchain.scripthash.subscribe"
        queue = asyncio.Queue()
        renew_task = asyncio.create_task(
            self.scripthash_renewer(scripthash, queue))

        while True:
            try:
                item = await queue.get()
                _ec, height, txid = struct.unpack("<HI32s", item)

                self.log.debug("shnotifier: _ec: %d", _ec)
                self.log.debug("shnotifier: height: %d", height)
                self.log.debug("shnotifier: txid: %s", hash_to_hex_str(txid))

                if (_ec == ZMQError.service_stopped.value and height == 0
                        and not self.stopped):
                    self.log.debug("subscription expired: %s", scripthash)
                    # Subscription expired
                    continue

                self.peers[self._get_peer(writer)]["sh"]["status"].append(
                    (hash_to_hex_str(txid), height))

                params = [
                    scripthash,
                    ElectrumProtocol.__scripthash_status_encode(self.peers[
                        self._get_peer(writer)]["sh"]["scripthash"]["status"]),
                ]
                await self._send_notification(writer, method, params)
            except asyncio.CancelledError:
                break
        renew_task.cancel()
Esempio n. 9
0
    async def blockchain_transaction_broadcast(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.transaction.broadcast
        Broadcast a transaction to the network.
        """
        # Note: Not yet implemented in bs v4
        if "params" not in query or len(query["params"]) != 1:
            return JsonRPCError.invalidparams()

        hextx = query["params"][0]
        if not is_hex_str(hextx):
            return JsonRPCError.invalidparams()

        _ec, _ = await self.bx.broadcast_transaction(unhexlify(hextx)[::-1])
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        rawtx = unhexlify(hextx)
        txid = double_sha256(rawtx)
        return {"result": hash_to_hex_str(txid)}
Esempio n. 10
0
    async def blockchain_transaction_id_from_pos(self, writer, query):  # pylint: disable=R0911,W0613
        """Method: blockchain.transaction.id_from_pos
        Return a transaction hash and optionally a merkle proof, given a
        block height and a position in the block.
        """
        if "params" not in query or len(query["params"]) < 2:
            return JsonRPCError.invalidparams()

        height = query["params"][0]
        tx_pos = query["params"][1]
        merkle = query["params"][2] if len(query["params"]) > 2 else False

        if not is_non_negative_integer(height):
            return JsonRPCError.invalidparams()
        if not is_non_negative_integer(tx_pos):
            return JsonRPCError.invalidparams()
        if not is_boolean(merkle):
            return JsonRPCError.invalidparams()

        _ec, hashes = await self.bx.fetch_block_transaction_hashes(height)
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        if len(hashes) - 1 < tx_pos:
            return JsonRPCError.internalerror()

        # Decouple from tuples
        hashes = [i[0] for i in hashes]
        txid = hash_to_hex_str(hashes[tx_pos])

        if not merkle:
            return {"result": txid}

        branch = merkle_branch(hashes, tx_pos)
        return {"result": {"tx_hash": txid, "merkle": branch}}
Esempio n. 11
0
def merkle_branch(tx_hashes, tx_pos):
    """Return a merkle branch given hashes and the tx position"""
    branch, _ = merkle_branch_and_root(tx_hashes, tx_pos)
    return [hash_to_hex_str(h) for h in branch]