Beispiel #1
0
    async def blockchain_transaction_get_merkle(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.transaction.get_merkle
        Return the merkle branch to a confirmed transaction given its
        hash and height.
        """
        if "params" not in query or len(query["params"]) != 2:
            return JsonRPCError.invalidparams()

        tx_hash = query["params"][0]
        height = query["params"][1]

        if not is_hash256_str(tx_hash):
            return JsonRPCError.invalidparams()
        if not is_non_negative_integer(height):
            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()

        # Decouple from tuples
        hashes = [i[0] for i in hashes]
        tx_pos = hashes.index(unhexlify(tx_hash)[::-1])
        branch = merkle_branch(hashes, tx_pos)

        res = {
            "block_height": int(height),
            "pos": int(tx_pos),
            "merkle": branch,
        }
        return {"result": res}
Beispiel #2
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}
Beispiel #3
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}
Beispiel #4
0
    async def 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 != ZMQError.success:
            self.log.error("bx.fetch_history4: %s", _ec.name)
            return JsonRPCError.internalerror()

        # TODO: Check how history4 acts for mempool/unconfirmed
        status = ElectrumProtocol.__scripthash_status_from_history(history)
        self.peers[self._get_peer(writer)]["sh"][scripthash] = {
            "status": status
        }

        task = asyncio.create_task(self.scripthash_notifier(
            writer, scripthash))
        self.peers[self._get_peer(writer)]["sh"][scripthash]["task"] = task

        if len(history) < 1:
            return {"result": None}
        return {"result": ElectrumProtocol.__scripthash_status_encode(status)}
Beispiel #5
0
    async def estimatefee(self, writer, query):  # pylint: disable=W0613,disable=R0911
        """Method: blockchain.estimatefee
        Return the estimated transaction fee per kilobyte for a transaction
        to be confirmed within a certain number of blocks.
        """
        # NOTE: This solution is using the mempool.space API.
        # Let's try to eventually solve it with some internal logic.
        if "params" not in query or len(query["params"]) != 1:
            return JsonRPCError.invalidparams()

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

        if self.chain == "testnet":
            return {"result": 0.00001}

        fee_dict = get_mempool_fee_estimates()
        if not fee_dict:
            return {"result": -1}

        # Good enough.
        if num_blocks < 3:
            return {"result": "{:.8f}".format(fee_dict["fastestFee"] / 100000)}

        if num_blocks < 6:
            return {
                "result": "{:.8f}".format(fee_dict["halfHourFee"] / 100000)
            }

        if num_blocks < 10:
            return {"result": "{:.8f}".format(fee_dict["hourFee"] / 100000)}

        return {"result": "{:.8f}".format(fee_dict["minimumFee"] / 100000)}
Beispiel #6
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}
Beispiel #7
0
    async def block_header(self, writer, query):  # pylint: disable=W0613,R0911
        """Method: blockchain.block.header
        Return the block header at the given height.
        """
        if "params" not in query or len(query["params"]) < 1:
            return JsonRPCError.invalidparams()
        index = query["params"][0]
        cp_height = query["params"][1] if len(query["params"]) == 2 else 0

        if not is_non_negative_integer(index):
            return JsonRPCError.invalidparams()
        if not is_non_negative_integer(cp_height):
            return JsonRPCError.invalidparams()
        if cp_height != 0 and not index <= cp_height:
            return JsonRPCError.invalidparams()

        if cp_height == 0:
            _ec, header = await self.bx.fetch_block_header(index)
            if _ec and _ec != ZMQError.success:
                self.log.error("bx.fetch_block_header: %s", _ec.name)
                return JsonRPCError.internalerror()
            return {"result": safe_hexlify(header)}

        res = await self._merkle_proof_for_headers(cp_height, index)
        return {"result": res}
Beispiel #8
0
    async def blockchain_transaction_get(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.transaction.get
        Return a raw transaction.
        """
        if "params" not in query or len(query["params"]) < 1:
            return JsonRPCError.invalidparams()

        tx_hash = query["params"][0]
        verbose = query["params"][1] if len(query["params"]) > 1 else False

        # _ec, rawtx = await self.bx.fetch_blockchain_transaction(tx_hash)
        _ec, rawtx = await self.bx.fetch_mempool_transaction(tx_hash)
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        # Behaviour is undefined in spec
        if not rawtx:
            return {"result": None}

        if verbose:
            # TODO: Help needed
            return JsonRPCError.invalidrequest()

        return {"result": bh2u(rawtx)}
Beispiel #9
0
    async def handle_query(self, writer, query):  # pylint: disable=R0915,R0912,R0911
        """Electrum protocol method handler mapper"""
        if "method" not in query or "id" not in query:
            return await self._send_reply(writer,
                                          JsonRPCError.invalidrequest(), None)

        method = query["method"]
        func = self.methodmap.get(method)
        if not func:
            self.log.error("Unhandled method %s, query=%s", method, query)
            return await self._send_reply(writer,
                                          JsonRPCError.methodnotfound(), query)
        resp = await func(writer, query)
        return await self._send_reply(writer, resp, query)
Beispiel #10
0
async def test_server_version(protocol, writer, method):
    params = ["obelisk 42", [SERVER_PROTO_MIN, SERVER_PROTO_MAX]]
    expect = {"result": [f"obelisk {VERSION}", SERVER_PROTO_MAX]}
    data = await protocol.server_version(writer, {"params": params})
    assert_equal(data["result"], expect["result"])

    params = ["obelisk", "0.0"]
    expect = JsonRPCError.protonotsupported()
    data = await protocol.server_version(writer, {"params": params})
    assert_equal(data, expect)

    params = ["obelisk"]
    expect = JsonRPCError.invalidparams()
    data = await protocol.server_version(writer, {"params": params})
    assert_equal(data, expect)
Beispiel #11
0
async def test_transaction_get_merkle(protocol, writer, method):
    params = [
        [
            "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20",
            1970700,
        ],
    ]
    for i in params:
        expect = get_expect(method, i)
        data = await protocol.transaction_get_merkle(writer, {"params": i})
        assert_equal(data["result"], expect["result"])

    params = [
        [],
        ["foo", 1],
        [3, 1],
        [
            "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20",
            -4,
        ],
        [
            "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20",
            "foo",
        ],
    ]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.transaction_get_merkle(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #12
0
async def test_scripthash_listunspent(protocol, writer, method):
    params = [
        ["c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921"],
        ["92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c"],
        ["b97b504af8fcf94a47d3ae5a346d38220f0751732d9b89a413568bfbf4b36ec6"],
    ]
    for i in params:
        # Blockstream is broken here and doesn't return in ascending order.
        expect = get_expect(method, i)
        srt = sorted(expect["result"], key=lambda x: x["height"])
        data = await protocol.scripthash_listunspent(writer, {"params": i})
        assert_equal(data["result"], srt)

    params = [
        [],
        ["foobar"],
        [
            "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
            42,
        ],
    ]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.scripthash_listunspent(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #13
0
    async def scripthash_get_balance(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.get_balance
        Return the confirmed and unconfirmed balances 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_balance(query["params"][0])
        if _ec and _ec != ZMQError.success:
            self.log.error("bx.fetch_balance: %s", _ec.name)
            return JsonRPCError.internalerror()

        ret = {"confirmed": data[0], "unconfirmed": data[1]}
        return {"result": ret}
Beispiel #14
0
    async def blockchain_headers_subscribe(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.headers.subscribe
        Subscribe to receive block headers when a new block is found.
        """
        # Tip height and header are returned upon request
        _ec, height = await self.bx.fetch_last_height()
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()
        _ec, tip_header = await self.bx.fetch_block_header(height)
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        self.tasks.append(asyncio.create_task(self.header_notifier(writer)))
        ret = {"height": height, "hex": safe_hexlify(tip_header)}
        return {"result": ret}
Beispiel #15
0
    async def blockchain_scripthash_get_balance(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.get_balance
        Return the confirmed and unconfirmed balances 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_balance(query["params"][0])
        if _ec and _ec != 0:
            self.log.debug("Got error: %s", repr(_ec))
            return JsonRPCError.internalerror()

        # TODO: confirmed/unconfirmed, see what's happening in libbitcoin
        ret = {"confirmed": data, "unconfirmed": 0}
        return {"result": ret}
Beispiel #16
0
    async def headers_subscribe(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.headers.subscribe
        Subscribe to receive block headers when a new block is found.
        """
        # Tip height and header are returned upon request
        _ec, height = await self.bx.fetch_last_height()
        if _ec and _ec != ZMQError.success:
            self.log.error("bx.fetch_last_height: %s", _ec.name)
            return JsonRPCError.internalerror()
        _ec, tip_header = await self.bx.fetch_block_header(height)
        if _ec and _ec != ZMQError.success:
            self.log.error("bx.fetch_block_header: %s", _ec.name)
            return JsonRPCError.internalerror()

        self.peers[self._get_peer(writer)]["tasks"].append(
            asyncio.create_task(self.header_notifier(writer)))
        ret = {"height": height, "hex": safe_hexlify(tip_header)}
        return {"result": ret}
Beispiel #17
0
    async def block_headers(self, writer, query):  # pylint: disable=W0613,R0911
        """Method: blockchain.block.headers
        Return a concatenated chunk of block headers from the main chain.
        """
        if "params" not in query or len(query["params"]) < 2:
            return JsonRPCError.invalidparams()
        # Electrum doesn't allow max_chunk_size to be less than 2016
        # gopher://bitreich.org/9/memecache/convenience-store.mkv
        max_chunk_size = 2016
        start_height = query["params"][0]
        count = query["params"][1]
        cp_height = query["params"][2] if len(query["params"]) == 3 else 0

        if not is_non_negative_integer(start_height):
            return JsonRPCError.invalidparams()
        if not is_non_negative_integer(count):
            return JsonRPCError.invalidparams()
        # BUG: spec says <= cp_height
        if cp_height != 0 and not start_height + (count - 1) < cp_height:
            return JsonRPCError.invalidparams()

        count = min(count, max_chunk_size)
        headers = bytearray()
        for i in range(count):
            _ec, data = await self.bx.fetch_block_header(start_height + i)
            if _ec and _ec != ZMQError.success:
                self.log.error("bx.fetch_block_header: %s", _ec.name)
                return JsonRPCError.internalerror()
            headers.extend(data)

        resp = {
            "hex": safe_hexlify(headers),
            "count": len(headers) // 80,
            "max": max_chunk_size,
        }

        if cp_height > 0:
            data = await self._merkle_proof_for_headers(
                cp_height, start_height + (len(headers) // 80) - 1)
            resp["branch"] = data["branch"]
            resp["root"] = data["root"]

        return {"result": resp}
Beispiel #18
0
    async def blockchain_scripthash_unsubscribe(self, writer, query):  # pylint: disable=W0613
        """Method: blockchain.scripthash.unsubscribe
        Unsubscribe from a script hash, preventing future notifications
        if its status changes.
        """
        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()

        if scripthash in self.sh_subscriptions:
            self.sh_subscriptions[scripthash]["task"].cancel()
            await self.bx.unsubscribe_scripthash(scripthash)
            del self.sh_subscriptions[scripthash]
            return {"result": True}

        return {"result": False}
Beispiel #19
0
    async def server_version(self, writer, query):  # pylint: disable=W0613
        """Method: server.version
        Identify the client to the server and negotiate the protocol version.
        """
        if "params" not in query or len(query["params"]) != 2:
            return JsonRPCError.invalidparams()

        client_ver = query["params"][1]

        if isinstance(client_ver, list):
            client_min, client_max = client_ver[0], client_ver[1]
        else:
            client_min = client_max = client_ver

        version = min(client_max, SERVER_PROTO_MAX)

        if version < max(client_min, SERVER_PROTO_MIN):
            return JsonRPCError.protonotsupported()

        return {"result": [f"obelisk {VERSION}", version]}
Beispiel #20
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)}
Beispiel #21
0
async def test_block_headers(protocol, writer, method):
    params = [[123, 3], [11, 3, 14]]
    for i in params:
        expect = get_expect(method, i)
        data = await protocol.block_headers(writer, {"params": i})
        assert_equal(data["result"], expect["result"])

    params = [[], [1], [-3, 1], [4, -1], [7, 4, 4]]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.block_headers(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #22
0
async def test_transaction_id_from_pos(protocol, writer, method):
    params = [[1970700, 28], [1970700, 28, True]]
    for i in params:
        expect = get_expect(method, i)
        data = await protocol.transaction_id_from_pos(writer, {"params": i})
        assert_equal(data["result"], expect["result"])

    params = [[123], [-1, 1], [1, -1], [3, 42, 4]]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.transaction_id_from_pos(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #23
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)}
Beispiel #24
0
    async def blockchain_block_headers(self, writer, query):  # pylint: disable=W0613,R0911
        """Method: blockchain.block.headers
        Return a concatenated chunk of block headers from the main chain.
        """
        if "params" not in query or len(query["params"]) < 2:
            return JsonRPCError.invalidparams()
        # Electrum doesn't allow max_chunk_size to be less than 2016
        # gopher://bitreich.org/9/memecache/convenience-store.mkv
        max_chunk_size = 2016
        start_height = query["params"][0]
        count = query["params"][1]
        cp_height = query["params"][2] if len(query["params"]) == 3 else 0

        if not is_non_negative_integer(start_height):
            return JsonRPCError.invalidparams()
        if not is_non_negative_integer(count):
            return JsonRPCError.invalidparams()
        if cp_height != 0 and not start_height + (count - 1) <= cp_height:
            return JsonRPCError.invalidparams()

        count = min(count, max_chunk_size)
        headers = bytearray()
        for i in range(count):
            _ec, data = await self.bx.fetch_block_header(start_height + i)
            if _ec and _ec != 0:
                self.log.debug("Got error: %s", repr(_ec))
                return JsonRPCError.internalerror()
            headers.extend(data)

        resp = {
            "hex": safe_hexlify(headers),
            "count": len(headers) // 80,
            "max": max_chunk_size,
        }

        # The assumption is to fetch more headers if necessary.
        # TODO: Review
        if cp_height > 0 and cp_height - start_height > count:
            for i in range(cp_height - start_height):
                _ec, data = await self.bx.fetch_block_header(start_height +
                                                             count + i)
                if _ec and _ec != 0:
                    self.log.debug("Got error: %s", repr(_ec))
                    return JsonRPCError.internalerror()
                headers.extend(data)

        if cp_height > 0:
            # TODO: Review
            # TODO: Is index is 0 or last elem?
            hdr_lst = [headers[i:i + 80] for i in range(0, len(headers), 80)]
            branch, root = merkle_branch_and_root(hdr_lst, 0)
            resp["branch"] = [safe_hexlify(i) for i in branch]
            resp["root"] = safe_hexlify(root)

        return {"result": resp}
Beispiel #25
0
async def test_scripthash_unsubscribe(protocol, writer, method):
    # Here blockstream doesn't even care
    params = [
        ["92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c"],
    ]
    for i in params:
        data = await protocol.scripthash_unsubscribe(writer, {"params": i})
        assert data["result"] is True

    params = [
        [],
        ["foobar"],
        [
            "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
            42,
        ],
    ]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.scripthash_unsubscribe(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #26
0
async def test_scripthash_get_history(protocol, writer, method):
    params = [
        ["c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921"],
        ["b97b504af8fcf94a47d3ae5a346d38220f0751732d9b89a413568bfbf4b36ec6"],
    ]
    for i in params:
        expect = get_expect(method, i)
        data = await protocol.scripthash_get_history(writer, {"params": i})
        assert_equal(data["result"], expect["result"])

    params = [
        [],
        ["foobar"],
        [
            "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
            42,
        ],
    ]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.scripthash_get_history(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #27
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),
        }
Beispiel #28
0
async def test_scripthash_get_balance(protocol, writer, method):
    params = [
        ["c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921"],
        ["92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c"],
        ["b97b504af8fcf94a47d3ae5a346d38220f0751732d9b89a413568bfbf4b36ec6"],
    ]
    for i in params:
        expect = get_expect(method, i)
        data = await protocol.scripthash_get_balance(writer, {"params": i})
        assert_equal(data["result"], expect["result"])

    params = [
        [],
        ["foobar"],
        [
            "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
            42,
        ],
    ]
    for i in params:
        expect = JsonRPCError.invalidparams()
        data = await protocol.scripthash_get_balance(writer, {"params": i})
        assert_equal(data, expect)
Beispiel #29
0
    async def blockchain_block_header(self, writer, query):  # pylint: disable=W0613,R0911
        """Method: blockchain.block.header
        Return the block header at the given height.
        """
        if "params" not in query or len(query["params"]) < 1:
            return JsonRPCError.invalidparams()
        index = query["params"][0]
        cp_height = query["params"][1] if len(query["params"]) == 2 else 0

        if not is_non_negative_integer(index):
            return JsonRPCError.invalidparams()
        if not is_non_negative_integer(cp_height):
            return JsonRPCError.invalidparams()
        if cp_height != 0 and not index <= cp_height:
            return JsonRPCError.invalidparams()

        if cp_height == 0:
            _ec, header = await self.bx.fetch_block_header(index)
            if _ec and _ec != 0:
                self.log.debug("Got error: %s", repr(_ec))
                return JsonRPCError.internalerror()
            return {"result": safe_hexlify(header)}

        cp_headers = []
        # headers up to and including cp_height
        for i in range(index, cp_height + 1):
            _ec, data = await self.bx.fetch_block_header(i)
            if _ec and _ec != 0:
                self.log.debug("Got error: %s", repr(_ec))
                return JsonRPCError.internalerror()
            cp_headers.append(data)

        # TODO: Review
        # TODO: Is index is 0 or last elem?
        branch, root = merkle_branch_and_root(cp_headers, 0)
        return {
            "result": {
                "branch": [safe_hexlify(i) for i in branch],
                "header": safe_hexlify(cp_headers[0]),
                "root": safe_hexlify(root),
            }
        }
Beispiel #30
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}}