commit f28cf131ba9a7bacfbffd4081e420ecd15e57a6f
parent 5bbe4f0c3658f82518243e59429f75c5acb31d34
Author: parazyd <parazyd@dyne.org>
Date: Thu, 15 Apr 2021 19:59:45 +0200
Refactor tests.
Diffstat:
4 files changed, 215 insertions(+), 163 deletions(-)
diff --git a/obelisk/merkle.py b/obelisk/merkle.py
@@ -22,10 +22,14 @@ from obelisk.util import double_sha256
def branch_length(hash_count):
"""Return the length of a merkle branch given the number of hashes"""
+ if not isinstance(hash_count, int):
+ raise TypeError("hash_count must be an integer")
+ if hash_count < 1:
+ raise ValueError("hash_count must be at least 1")
return ceil(log(hash_count, 2))
-def merkle_branch_and_root(hashes, index):
+def merkle_branch_and_root(hashes, index, length=None):
"""Return a (merkle branch, merkle_root) pair given hashes, and the
index of one of those hashes.
"""
@@ -35,7 +39,14 @@ def merkle_branch_and_root(hashes, index):
# This also asserts hashes is not empty
if not 0 <= index < len(hashes):
raise ValueError("index out of range")
- length = branch_length(len(hashes))
+ natural_length = branch_length(len(hashes))
+ if length is None:
+ length = natural_length
+ else:
+ if not isinstance(length, int):
+ raise TypeError("length must be an integer")
+ if length < natural_length:
+ raise ValueError("length out of range")
branch = []
for _ in range(length):
@@ -52,6 +63,6 @@ def merkle_branch_and_root(hashes, index):
def merkle_branch(tx_hashes, tx_pos):
"""Return a merkle branch given hashes and the tx position"""
- branch, _root = merkle_branch_and_root(tx_hashes, tx_pos)
+ branch, _ = merkle_branch_and_root(tx_hashes, tx_pos)
branch = [bytes(reversed(h)).hex() for h in branch]
return branch
diff --git a/obelisk/protocol.py b/obelisk/protocol.py
@@ -108,7 +108,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
"blockchain.transaction.get_merkle":
self.blockchain_transaction_get_merkle,
"blockchain.transaction.id_from_pos":
- self.blockchain_transaction_from_pos,
+ self.blockchain_transaction_id_from_pos,
"mempool.get_fee_histogram":
self.mempool_get_fee_histogram,
"server_add_peer":
@@ -451,6 +451,9 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
"tx_hash": hash_to_hex_str(rec["hash"]),
"height": rec["height"],
})
+
+ if len(ret) >= 2:
+ ret.reverse()
return {"result": ret}
async def scripthash_notifier(self, writer, scripthash):
@@ -610,7 +613,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902
}
return {"result": res}
- async def blockchain_transaction_from_pos(self, writer, query): # pylint: disable=R0911,W0613
+ 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.
diff --git a/res/format_code.py b/res/format_code.py
@@ -5,5 +5,5 @@
# yapf - https://github.com/google/yapf
from subprocess import run
-run(["black", "-l", "80", "."])
-run(["yapf", "--style", "google", "-i", "-r", "."])
+run(["black", "-l", "80", "."], check=True)
+run(["yapf", "--style", "google", "-i", "-r", "."], check=True)
diff --git a/tests/test_electrum_protocol.py b/tests/test_electrum_protocol.py
@@ -14,167 +14,198 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Test unit for the Electrum protocol. Takes results from testnet
+blockstream.info:143 server as value reference.
+
+See bottom of file for test orchestration.
+"""
import asyncio
+import json
import sys
+import traceback
from logging import getLogger
+from pprint import pprint
+from socket import socket, AF_INET, SOCK_STREAM
from obelisk.protocol import ElectrumProtocol
+from obelisk.zeromq import create_random_id
-#
-# See bottom of this file for test orchestration.
-#
-
-ENDPOINTS = {
+libbitcoin = {
"query": "tcp://testnet2.libbitcoin.net:29091",
"heart": "tcp://testnet2.libbitcoin.net:29092",
"block": "tcp://testnet2.libbitcoin.net:29093",
"trans": "tcp://testnet2.libbitcoin.net:29094",
}
+blockstream = ("blockstream.info", 143)
+bs = None # Socket
-async def test_blockchain_block_header(protocol, writer):
- expect = "01000000c54675276e0401706aa93db6494dd7d1058b19424f23c8d7c01076da000000001c4375c8056b0ded0fa3d7fc1b5511eaf53216aed72ea95e1b5d19eccbe855f91a184a4dffff001d0336a226"
- query = {"params": [123]}
- data = await protocol.blockchain_block_header(writer, query)
- assert data["result"] == expect
+def get_expect(method, params):
+ global bs
+ req = {
+ "json-rpc": "2.0",
+ "id": create_random_id(),
+ "method": method,
+ "params": params,
+ }
+ bs.send(json.dumps(req).encode("utf-8") + b"\n")
+ recv_buf = bytearray()
+ while True:
+ data = bs.recv(4096)
+ if not data or len(data) == 0:
+ raise ValueError("No data received from blockstream")
+ recv_buf.extend(data)
+ lb = recv_buf.find(b"\n")
+ if lb == -1:
+ continue
+ while lb != -1:
+ line = recv_buf[:lb].rstrip()
+ recv_buf = recv_buf[lb + 1:]
+ lb = recv_buf.find(b"\n")
+ line = line.decode("utf-8")
+ resp = json.loads(line)
+ return resp
-async def test_blockchain_block_headers(protocol, writer):
- expect = "01000000c54675276e0401706aa93db6494dd7d1058b19424f23c8d7c01076da000000001c4375c8056b0ded0fa3d7fc1b5511eaf53216aed72ea95e1b5d19eccbe855f91a184a4dffff001d0336a22601000000bca72b7ccb44f1f0dd803f2c321143c9dda7f5a2a6ed87c76aac918a000000004266985f02f11bdffa559a233f5600c95c04bd70340e75673cadaf3ef6ac72b448194a4dffff001d035c84d801000000769d6d6e4672a620669baa56dd39d066523e461762ad3610fb2055b400000000c50652340352ad79b799b870e3fa2c80804d0fc54063b413e0e2d6dc66ca3f9a55194a4dffff001d022510a4"
- query = {"params": [123, 3]}
- data = await protocol.blockchain_block_headers(writer, query)
- assert data["result"]["hex"] == expect
+async def test_blockchain_block_header(protocol, writer):
+ method = "blockchain.block.header"
+ params = [123]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_block_header(writer, {"params": params})
+ assert data["result"] == expect["result"]
+
+ # params = [123, 130]
+ # expect = get_expect(method, params)
+ # data = await protocol.blockchain_block_header(writer, {"params": params})
+
+ # assert data["result"]["header"] == expect["result"]["header"]
+ # assert data["result"]["branch"] == expect["result"]["branch"]
+ # assert data["result"]["root"] == expect["result"]["root"]
-async def test_blockchain_estimatefee(protocol, writer):
- expect = -1
- query = {"params": []}
- data = await protocol.blockchain_estimatefee(writer, query)
- assert data["result"] == expect
+async def test_blockchain_block_headers(protocol, writer):
+ method = "blockchain.block.headers"
+ params = [123, 3]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_block_headers(writer, {"params": params})
+ assert data["result"]["hex"] == expect["result"]["hex"]
-async def test_blockchain_relayfee(protocol, writer):
- expect = 0.00001
- query = {"params": []}
- data = await protocol.blockchain_relayfee(writer, query)
- assert data["result"] == expect
+ # params = [123, 3, 127]
+ # expect = get_expect(method, params)
+ # data = await protocol.blockchain_block_headers(writer, {"params": params})
+ # assert data["result"]["branch"] == expect["result"]["branch"]
+ # assert data["result"]["root"] == expect["result"]["root"]
+ # assert data["result"]["hex"] == expect["result"]["hex"]
async def test_blockchain_scripthash_get_balance(protocol, writer):
- shs = [
- "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
- "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c",
+ method = "blockchain.scripthash.get_balance"
+ params = [
+ "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921"
]
- expect = [
- {
- "result": {
- "confirmed": 0,
- "unconfirmed": 0
- }
- },
- {
- "result": {
- "confirmed": 831000,
- "unconfirmed": 0
- }
- },
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_scripthash_get_balance(
+ writer, {"params": params})
+ assert data["result"]["unconfirmed"] == expect["result"]["unconfirmed"]
+ assert data["result"]["confirmed"] == expect["result"]["confirmed"]
+
+ params = [
+ "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c"
]
-
- data = []
- for i in shs:
- params = {"params": [i]}
- data.append(await
- protocol.blockchain_scripthash_get_balance(writer, params))
-
- for i in expect:
- assert data[expect.index(i)] == i
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_scripthash_get_balance(
+ writer, {"params": params})
+ assert data["result"]["unconfirmed"] == expect["result"]["unconfirmed"]
+ assert data["result"]["confirmed"] == expect["result"]["confirmed"]
async def test_blockchain_scripthash_get_history(protocol, writer):
- shs = [
- "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
- "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c",
+ method = "blockchain.scripthash.get_history"
+ params = [
+ "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921"
]
- expect = [
- (
- 1936167,
- "084eba0e08c78b63e07535b74a5a849994d49afade95d0d205e4963e3f568600",
- ),
- (
- 1936171,
- "705c4f265df23726c09c5acb80f9e8a85845c17d68974d89814383855c8545a2",
- ),
- (
- 1936171,
- "705c4f265df23726c09c5acb80f9e8a85845c17d68974d89814383855c8545a2",
- ),
- (
- 1970700,
- "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20",
- ),
- ]
-
- res = []
- for i in shs:
- params = {"params": [i]}
- data = await protocol.blockchain_scripthash_get_history(writer, params)
- if "result" in data:
- for j in data["result"]:
- res.append((j["height"], j["tx_hash"]))
-
- assert res == expect
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_scripthash_get_history(
+ writer, {"params": params})
+ assert len(data["result"]) == len(expect["result"])
+ for i in range(len(expect["result"])):
+ assert data["result"][i]["tx_hash"] == expect["result"][i]["tx_hash"]
+ assert data["result"][i]["height"] == expect["result"][i]["height"]
async def test_blockchain_scripthash_listunspent(protocol, writer):
- shs = [
- "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921",
- "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c",
+ method = "blockchain.scripthash.listunspent"
+ params = [
+ "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921"
]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_scripthash_listunspent(
+ writer, {"params": params})
+ assert data["result"] == expect["result"]
- expect = [
- [],
- [1, 731000, 1936171],
- [1, 100000, 1970700],
+ params = [
+ "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c"
]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_scripthash_listunspent(
+ writer, {"params": params})
- res = []
- for i in shs:
- params = {"params": [i]}
- data = await protocol.blockchain_scripthash_listunspent(writer, params)
- if "result" in data and len(data["result"]) > 0:
- for j in data["result"]:
- res.append([j["tx_pos"], j["value"], j["height"]])
- else:
- res.append([])
-
- assert res == expect
+ assert len(data["result"]) == len(expect["result"])
+ for i in range(len(expect["result"])):
+ assert data["result"][i]["value"] == expect["result"][i]["value"]
+ assert data["result"][i]["height"] == expect["result"][i]["height"]
+ assert data["result"][i]["tx_pos"] == expect["result"][i]["tx_pos"]
+ assert data["result"][i]["tx_hash"] == expect["result"][i]["tx_hash"]
async def test_blockchain_transaction_get(protocol, writer):
- expect = "020000000001011caa5f4ba91ff0ab77712851c1b17943e68f28d46bb0d96cbc13cdbef53c2b87000000001716001412e6e94028ab399b67c1232383d12f1dd3fc03b5feffffff02a40111000000000017a914ff1d7f4c85c562764ca16daa11e97d10eda52ebf87a0860100000000001976a9144a0360eac874a569e82ca6b17274d90bccbcab5e88ac0247304402205392417f5ffba2c0f3a501476fb6872368b2065c53bf18b2a201691fb88cdbe5022016c68ec9e094ba2b06d4bdc6af996ac74b580ab9728c622bb5304aaff04cb6980121031092742ffdf5901ceafcccec090c58170ce1d0ec26963ef7c7a2738a415a317e0b121e00"
- params = {
- "params": [
- "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20"
- ]
- }
- data = await protocol.blockchain_transaction_get(writer, params)
- assert data["result"] == expect
+ method = "blockchain.transaction.get"
+ params = [
+ "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20"
+ ]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_transaction_get(writer, {"params": params})
+ assert data["result"] == expect["result"]
-async def test_blockchain_transaction_from_pos(protocol, writer):
- expect = "f50f1c9b9551db0cc6916cb590bb6ccb5dea8adcb40e0bc103c4440e04c95e3d"
- params = {"params": [1839411, 0]}
- data = await protocol.blockchain_transaction_from_pos(writer, params)
- assert data["result"] == expect
- return "blockchain_transaction_from_pos", True
+async def test_blockchain_transaction_get_merkle(protocol, writer):
+ method = "blockchain.transaction.get_merkle"
+ params = [
+ "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20",
+ 1970700,
+ ]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_transaction_get_merkle(
+ writer, {"params": params})
+ assert data["result"]["block_height"] == expect["result"]["block_height"]
+ assert data["result"]["merkle"] == expect["result"]["merkle"]
+ assert data["result"]["pos"] == expect["result"]["pos"]
+
+
+async def test_blockchain_transaction_id_from_pos(protocol, writer):
+ method = "blockchain.transaction.id_from_pos"
+ params = [1970700, 28]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_transaction_id_from_pos(
+ writer, {"params": params})
+ assert data["result"] == expect["result"]
+
+ params = [1970700, 28, True]
+ expect = get_expect(method, params)
+ data = await protocol.blockchain_transaction_id_from_pos(
+ writer, {"params": params})
+ assert data["result"]["tx_hash"] == expect["result"]["tx_hash"]
+ assert data["result"]["merkle"] == expect["result"]["merkle"]
async def test_server_ping(protocol, writer):
- expect = None
- params = {"params": []}
- data = await protocol.server_ping(writer, params)
- assert data["result"] == expect
- return "server_ping", True
+ method = "server.ping"
+ params = []
+ expect = get_expect(method, params)
+ data = await protocol.server_ping(writer, {"params": params})
+ assert data["result"] == expect["result"]
class MockWriter(asyncio.StreamWriter):
@@ -190,58 +221,65 @@ class MockWriter(asyncio.StreamWriter):
return True
+# Test orchestration
+orchestration = {
+ "blockchain_block_header":
+ test_blockchain_block_header,
+ "blockchain_block_headers":
+ test_blockchain_block_headers,
+ # "blockchain_estimatefee": test_blockchain_estimatefee,
+ # "blockchain_headers_subscribe": test_blockchain_headers_subscribe,
+ # "blockchain_relayfee": test_blockchain_relayfee,
+ "blockchain_scripthash_get_balance":
+ test_blockchain_scripthash_get_balance,
+ "blockchain_scripthash_get_history":
+ test_blockchain_scripthash_get_history,
+ # "blockchain_scripthash_get_mempool": test_blockchain_scripthash_get_mempool,
+ "blockchain_scripthash_listunspent":
+ test_blockchain_scripthash_listunspent,
+ # "blockchain_scripthash_subscribe": test_blockchain_scripthash_subscribe,
+ # "blockchain_scripthash_unsubscribe": test_blockchain_scripthash_unsubscribe,
+ # "blockchain_transaction_broadcast": test_blockchain_transaction_broadcast,
+ "blockchain_transaction_get":
+ test_blockchain_transaction_get,
+ "blockchain_transaction_get_merkle":
+ test_blockchain_transaction_get_merkle,
+ "blockchain_transaction_id_from_pos":
+ test_blockchain_transaction_id_from_pos,
+ # "mempool_get_fee_histogram": test_mempool_get_fee_histogram,
+ # "server_add_peer": test_server_add_peer,
+ # "server_donation_address": test_server_donation_address,
+ # "server_features": test_server_features,
+ # "server_peers_subscribe": test_server_peers_subscribe,
+ "server_ping":
+ test_server_ping,
+ # "server_version": test_server_version,
+}
+
+
async def main():
test_pass = []
test_fail = []
+ global bs
+ bs = socket(AF_INET, SOCK_STREAM)
+ bs.connect(blockstream)
+
log = getLogger("obelisktest")
- protocol = ElectrumProtocol(log, "testnet", ENDPOINTS, {})
+ protocol = ElectrumProtocol(log, "testnet", libbitcoin, {})
writer = MockWriter()
- functions = {
- "blockchain_block_header":
- test_blockchain_block_header,
- "blockchain_block_hedaers":
- test_blockchain_block_headers,
- "blockchain_estimatefee":
- test_blockchain_estimatefee,
- # "blockchain_headers_subscribe": test_blockchain_headers_subscribe,
- "blockchain_relayfee":
- test_blockchain_relayfee,
- "blockchain_scripthash_get_balance":
- test_blockchain_scripthash_get_balance,
- "blockchain_scripthash_get_history":
- test_blockchain_scripthash_get_history,
- # "blockchain_scripthash_get_mempool": test_blockchain_scripthash_get_mempool,
- "blockchain_scripthash_listunspent":
- test_blockchain_scripthash_listunspent,
- # "blockchain_scripthash_subscribe": test_blockchain_scripthash_subscribe,
- # "blockchain_scripthash_unsubscribe": test_blockchain_scripthash_unsubscribe,
- # "blockchain_transaction_broadcast": test_blockchain_transaction_broadcast,
- "blockchain_transaction_get":
- test_blockchain_transaction_get,
- # "blockchain_transaction_get_merkle": test_blockchain_transaction_get_merkle,
- "blockchain_transaction_from_pos":
- test_blockchain_transaction_from_pos,
- # "mempool_get_fee_histogram": test_mempool_get_fee_histogram,
- # "server_add_peer": test_server_add_peer,
- # "server_banner": test_server_banner,
- # "server_donation_address": test_server_donation_address,
- # "server_features": test_server_features,
- # "server_peers_subscribe": test_server_peers_subscribe,
- "server_ping":
- test_server_ping,
- # "server_version": test_server_version,
- }
- for func in functions:
+ for func in orchestration:
try:
- await functions[func](protocol, writer)
+ await orchestration[func](protocol, writer)
print(f"PASS: {func}")
test_pass.append(func)
except AssertionError:
print(f"FAIL: {func}")
+ traceback.print_exc()
test_fail.append(func)
+ bs.close()
await protocol.stop()
print()