commit c1161ee71243b8109c3dea742ec7c2081f1db264
parent b5e4ed32fabaa3d57057f12114be7aaa22dc0124
Author: parazyd <parazyd@dyne.org>
Date: Fri, 16 Apr 2021 00:34:43 +0200
Work out an inefficient implementation for header merkle proofs.
The proper solution would be to have something like this in libbitcoin.
Diffstat:
2 files changed, 40 insertions(+), 48 deletions(-)
diff --git a/obelisk/protocol.py b/obelisk/protocol.py
@@ -218,6 +218,29 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
resp = await func(writer, query)
return await self._send_reply(writer, resp, query)
+ 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 != 0:
+ self.log.debug("Got error: %s", repr(_ec))
+ 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),
+ }
+
async def blockchain_block_header(self, writer, query): # pylint: disable=W0613,R0911
"""Method: blockchain.block.header
Return the block header at the given height.
@@ -241,26 +264,8 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
return JsonRPCError.internalerror()
return {"result": safe_hexlify(header)}
- # 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, 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)
-
- hashed = [double_sha256(i) for i in cp_headers]
- branch, root = merkle_branch_and_root(hashed, index)
- return {
- "result": {
- "branch": [hash_to_hex_str(i) for i in branch],
- "header": safe_hexlify(cp_headers[index]),
- "root": hash_to_hex_str(root),
- }
- }
+ res = await self._merkle_proof_for_headers(cp_height, index)
+ return {"result": res}
async def blockchain_block_headers(self, writer, query): # pylint: disable=W0613,R0911
"""Method: blockchain.block.headers
@@ -279,7 +284,8 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
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:
+ # 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)
@@ -296,25 +302,14 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
"count": len(headers) // 80,
"max": max_chunk_size,
}
- return {"result": resp}
-
- # The assumption is to fetch more headers if necessary.
- # TODO: Review everything below, help needed
- return JsonRPCError.invalidrequest()
- 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:
- 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)
+ 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}
async def blockchain_estimatefee(self, writer, query): # pylint: disable=W0613
"""Method: blockchain.estimatefee
diff --git a/tests/test_electrum_protocol.py b/tests/test_electrum_protocol.py
@@ -76,7 +76,7 @@ async def test_blockchain_block_header(protocol, writer):
data = await protocol.blockchain_block_header(writer, {"params": params})
assert data["result"] == expect["result"]
- params = [13, 25]
+ params = [1, 5]
expect = get_expect(method, params)
data = await protocol.blockchain_block_header(writer, {"params": params})
assert data["result"] == expect["result"]
@@ -89,13 +89,11 @@ async def test_blockchain_block_headers(protocol, writer):
data = await protocol.blockchain_block_headers(writer, {"params": params})
assert data["result"] == expect["result"]
- # params = [123, 3, 127]
- # expect = get_expect(method, params)
- # data = await protocol.blockchain_block_headers(writer, {"params": params})
- # pprint(expect)
- # print()
- # pprint(data)
- # assert data["result"] == expect["result"]
+ # params = [1, 4, 11]
+ params = [11, 3, 14]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_block_headers(writer, {"params": params})
+ assert data["result"] == expect["result"]
async def test_blockchain_scripthash_get_balance(protocol, writer):
@@ -162,13 +160,12 @@ async def test_blockchain_scripthash_subscribe(protocol, writer):
async def test_blockchain_scripthash_unsubscribe(protocol, writer):
# Here blockstream doesn't even care
- method = "blockchain.scripthash.unsubscribe"
params = [
"92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c"
]
data = await protocol.blockchain_scripthash_unsubscribe(
writer, {"params": params})
- assert data["result"] == True
+ assert data["result"] is True
async def test_blockchain_transaction_get(protocol, writer):