obelisk

Electrum server using libbitcoin as its backend
git clone https://git.parazyd.org/obelisk
Log | Files | Refs | README | LICENSE

commit 65a5969065dbc8f20a16f854381644f31382d187
parent d6023f57b402de645449ff07b2375472afc06af6
Author: parazyd <parazyd@dyne.org>
Date:   Fri, 16 Apr 2021 02:04:30 +0200

Shorten protocol function names.

Diffstat:
Mobelisk/errors_jsonrpc.py | 2+-
Mobelisk/protocol.py | 112+++++++++++++++++++++++++++++++------------------------------------------------
Mtests/test_electrum_protocol.py | 138++++++++++++++++++++++++++++++-------------------------------------------------
3 files changed, 97 insertions(+), 155 deletions(-)

diff --git a/obelisk/errors_jsonrpc.py b/obelisk/errors_jsonrpc.py @@ -48,6 +48,6 @@ class JsonRPCError: return { "error": { "code": -32100, - "message": "protocol version unsupported" + "message": "protocol version unsupported", } } diff --git a/obelisk/protocol.py b/obelisk/protocol.py @@ -79,52 +79,29 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 # Here we map available methods to their respective functions self.methodmap = { - "blockchain.block.header": - self.blockchain_block_header, - "blockchain.block.headers": - self.blockchain_block_headers, - "blockchain.estimatefee": - self.blockchain_estimatefee, - "blockchain.headers.subscribe": - self.blockchain_headers_subscribe, - "blockchain.relayfee": - self.blockchain_relayfee, - "blockchain.scripthash.get_balance": - self.blockchain_scripthash_get_balance, - "blockchain.scripthash.get_history": - self.blockchain_scripthash_get_history, - "blockchain.scripthash.get_mempool": - self.blockchain_scripthash_get_mempool, - "blockchain.scripthash.listunspent": - self.blockchain_scripthash_listunspent, - "blockchain.scripthash.subscribe": - self.blockchain_scripthash_subscribe, - "blockchain.scripthash.unsubscribe": - self.blockchain_scripthash_unsubscribe, - "blockchain.transaction.broadcast": - self.blockchain_transaction_broadcast, - "blockchain.transaction.get": - self.blockchain_transaction_get, - "blockchain.transaction.get_merkle": - self.blockchain_transaction_get_merkle, - "blockchain.transaction.id_from_pos": - self.blockchain_transaction_id_from_pos, - "mempool.get_fee_histogram": - self.mempool_get_fee_histogram, - "server_add_peer": - self.server_add_peer, - "server.banner": - self.server_banner, - "server.donation_address": - self.server_donation_address, - "server.features": - self.server_features, - "server.peers.subscribe": - self.server_peers_subscribe, - "server.ping": - self.server_ping, - "server.version": - self.server_version, + "blockchain.block.header": self.block_header, + "blockchain.block.headers": self.block_headers, + "blockchain.estimatefee": self.estimatefee, + "blockchain.headers.subscribe": self.headers_subscribe, + "blockchain.relayfee": self.relayfee, + "blockchain.scripthash.get_balance": self.scripthash_get_balance, + "blockchain.scripthash.get_history": self.scripthash_get_history, + "blockchain.scripthash.get_mempool": self.scripthash_get_mempool, + "blockchain.scripthash.listunspent": self.scripthash_listunspent, + "blockchain.scripthash.subscribe": self.scripthash_subscribe, + "blockchain.scripthash.unsubscribe": self.scripthash_unsubscribe, + "blockchain.transaction.broadcast": self.transaction_broadcast, + "blockchain.transaction.get": self.transaction_get, + "blockchain.transaction.get_merkle": self.transaction_get_merkle, + "blockchain.transaction.id_from_pos": self.transaction_id_from_pos, + "mempool.get_fee_histogram": self.get_fee_histogram, + "server_add_peer": self.add_peer, + "server.banner": self.banner, + "server.donation_address": self.donation_address, + "server.features": self.server_features, + "server.peers.subscribe": self.peers_subscribe, + "server.ping": self.ping, + "server.version": self.server_version, } async def stop(self): @@ -241,7 +218,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 "root": hash_to_hex_str(root), } - async def blockchain_block_header(self, writer, query): # pylint: disable=W0613,R0911 + async def block_header(self, writer, query): # pylint: disable=W0613,R0911 """Method: blockchain.block.header Return the block header at the given height. """ @@ -267,7 +244,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 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 + 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. """ @@ -311,7 +288,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 return {"result": resp} - async def blockchain_estimatefee(self, writer, query): # pylint: disable=W0613 + async def estimatefee(self, writer, query): # pylint: disable=W0613 """Method: blockchain.estimatefee Return the estimated transaction fee per kilobyte for a transaction to be confirmed within a certain number of blocks. @@ -335,7 +312,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 "blockchain.headers.subscribe", params) - async def blockchain_headers_subscribe(self, writer, query): # pylint: disable=W0613 + 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. """ @@ -353,15 +330,14 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 ret = {"height": height, "hex": safe_hexlify(tip_header)} return {"result": ret} - async def blockchain_relayfee(self, writer, query): # pylint: disable=W0613 + async def relayfee(self, writer, query): # pylint: disable=W0613 """Method: blockchain.relayfee Return the minimum fee a low-priority transaction must pay in order to be accepted to the daemon’s memory pool. """ - # TODO: Help wanted return {"result": 0.00001} - async def blockchain_scripthash_get_balance(self, writer, query): # pylint: disable=W0613 + 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. """ @@ -380,7 +356,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 ret = {"confirmed": data, "unconfirmed": 0} return {"result": ret} - async def blockchain_scripthash_get_history(self, writer, query): # pylint: disable=W0613 + async def scripthash_get_history(self, writer, query): # pylint: disable=W0613 """Method: blockchain.scripthash.get_history Return the confirmed and unconfirmed history of a script hash. """ @@ -412,14 +388,14 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 return {"result": ret} - async def blockchain_scripthash_get_mempool(self, writer, query): # pylint: disable=W0613 + async def scripthash_get_mempool(self, writer, query): # pylint: disable=W0613 """Method: blockchain.scripthash.get_mempool Return the unconfirmed transactions of a script hash. """ # TODO: Implement return JsonRPCError.invalidrequest() - async def blockchain_scripthash_listunspent(self, writer, query): # pylint: disable=W0613 + 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. """ @@ -460,7 +436,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 item = await sh_queue.get() self.log.debug("sh_subscription item: %s", item) - async def blockchain_scripthash_subscribe(self, writer, query): # pylint: disable=W0613 + async def scripthash_subscribe(self, writer, query): # pylint: disable=W0613 """Method: blockchain.scripthash.subscribe Subscribe to a script hash. """ @@ -505,7 +481,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 concat += txid + ":%d:" % height return bh2u(sha256(concat.encode("ascii"))) - async def blockchain_scripthash_unsubscribe(self, writer, query): # pylint: disable=W0613 + async def scripthash_unsubscribe(self, writer, query): # pylint: disable=W0613 """Method: blockchain.scripthash.unsubscribe Unsubscribe from a script hash, preventing future notifications if its status changes. @@ -525,7 +501,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 return {"result": False} - async def blockchain_transaction_broadcast(self, writer, query): # pylint: disable=W0613 + async def transaction_broadcast(self, writer, query): # pylint: disable=W0613 """Method: blockchain.transaction.broadcast Broadcast a transaction to the network. """ @@ -546,7 +522,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 txid = double_sha256(rawtx) return {"result": hash_to_hex_str(txid)} - async def blockchain_transaction_get(self, writer, query): # pylint: disable=W0613 + async def transaction_get(self, writer, query): # pylint: disable=W0613 """Method: blockchain.transaction.get Return a raw transaction. """ @@ -572,7 +548,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 return {"result": bh2u(rawtx)} - async def blockchain_transaction_get_merkle(self, writer, query): # pylint: disable=W0613 + async def 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. @@ -605,7 +581,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 } return {"result": res} - async def blockchain_transaction_id_from_pos(self, writer, query): # pylint: disable=R0911,W0613 + async def 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. @@ -642,7 +618,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 branch = merkle_branch(hashes, tx_pos) return {"result": {"tx_hash": txid, "merkle": branch}} - async def mempool_get_fee_histogram(self, writer, query): # pylint: disable=W0613 + async def get_fee_histogram(self, writer, query): # pylint: disable=W0613 """Method: mempool.get_fee_histogram Return a histogram of the fee rates paid by transactions in the memory pool, weighted by transaction size. @@ -650,7 +626,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 # TODO: Help wanted return {"result": [[0, 0]]} - async def server_add_peer(self, writer, query): # pylint: disable=W0613 + async def add_peer(self, writer, query): # pylint: disable=W0613 """Method: server.add_peer A newly-started server uses this call to get itself into other servers’ peers lists. It should not be used by wallet clients. @@ -658,13 +634,13 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 # TODO: Help wanted return {"result": False} - async def server_banner(self, writer, query): # pylint: disable=W0613 + async def banner(self, writer, query): # pylint: disable=W0613 """Method: server.banner Return a banner to be shown in the Electrum console. """ return {"result": BANNER} - async def server_donation_address(self, writer, query): # pylint: disable=W0613 + async def donation_address(self, writer, query): # pylint: disable=W0613 """Method: server.donation_address Return a server donation address. """ @@ -692,7 +668,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 } } - async def server_peers_subscribe(self, writer, query): # pylint: disable=W0613 + async def peers_subscribe(self, writer, query): # pylint: disable=W0613 """Method: server.peers.subscribe Return a list of peer servers. Despite the name this is not a subscription and the server must send no notifications. @@ -700,7 +676,7 @@ class ElectrumProtocol(asyncio.Protocol): # pylint: disable=R0904,R0902 # TODO: Help wanted return {"result": []} - async def server_ping(self, writer, query): # pylint: disable=W0613 + async def ping(self, writer, query): # pylint: disable=W0613 """Method: server.ping Ping the server to ensure it is responding, and to keep the session alive. The server may disconnect clients that have sent no requests diff --git a/tests/test_electrum_protocol.py b/tests/test_electrum_protocol.py @@ -80,79 +80,68 @@ def assert_equal(data, expect): raise -async def test_blockchain_block_header(protocol, writer): - method = "blockchain.block.header" +async def test_block_header(protocol, writer, method): params = [123] expect = get_expect(method, params) - data = await protocol.blockchain_block_header(writer, {"params": params}) + data = await protocol.block_header(writer, {"params": params}) assert_equal(data["result"], expect["result"]) params = [1, 5] expect = get_expect(method, params) - data = await protocol.blockchain_block_header(writer, {"params": params}) + data = await protocol.block_header(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_block_headers(protocol, writer): - method = "blockchain.block.headers" +async def test_block_headers(protocol, writer, method): params = [123, 3] expect = get_expect(method, params) - data = await protocol.blockchain_block_headers(writer, {"params": params}) + data = await protocol.block_headers(writer, {"params": params}) assert_equal(data["result"], expect["result"]) params = [11, 3, 14] expect = get_expect(method, params) - data = await protocol.blockchain_block_headers(writer, {"params": params}) + data = await protocol.block_headers(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_headers_subscribe(protocol, writer): - method = "blockchain.headers.subscribe" +async def test_headers_subscribe(protocol, writer, method): params = [] expect = get_expect(method, params) - data = await protocol.blockchain_headers_subscribe(writer, - {"params": params}) + data = await protocol.headers_subscribe(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_scripthash_get_balance(protocol, writer): - method = "blockchain.scripthash.get_balance" +async def test_scripthash_get_balance(protocol, writer, method): params = [ "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921" ] expect = get_expect(method, params) - data = await protocol.blockchain_scripthash_get_balance( - writer, {"params": params}) + data = await protocol.scripthash_get_balance(writer, {"params": params}) assert_equal(data["result"], expect["result"]) params = [ "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c" ] expect = get_expect(method, params) - data = await protocol.blockchain_scripthash_get_balance( - writer, {"params": params}) + data = await protocol.scripthash_get_balance(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_scripthash_get_history(protocol, writer): - method = "blockchain.scripthash.get_history" +async def test_scripthash_get_history(protocol, writer, method): params = [ "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921" ] expect = get_expect(method, params) - data = await protocol.blockchain_scripthash_get_history( - writer, {"params": params}) + data = await protocol.scripthash_get_history(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_scripthash_listunspent(protocol, writer): - method = "blockchain.scripthash.listunspent" +async def test_scripthash_listunspent(protocol, writer, method): params = [ "c036b0ff3ad79662cd517cd5fe1fa0af07377b9262d16f276f11ced69aaa6921" ] expect = get_expect(method, params) - data = await protocol.blockchain_scripthash_listunspent( - writer, {"params": params}) + data = await protocol.scripthash_listunspent(writer, {"params": params}) assert_equal(data["result"], expect["result"]) params = [ @@ -161,74 +150,63 @@ async def test_blockchain_scripthash_listunspent(protocol, writer): # Blockstream is broken here and doesn't return in ascending order. expect = get_expect(method, params) srt = sorted(expect["result"], key=lambda x: x["height"]) - data = await protocol.blockchain_scripthash_listunspent( - writer, {"params": params}) + data = await protocol.scripthash_listunspent(writer, {"params": params}) assert_equal(data["result"], srt) -async def test_blockchain_scripthash_subscribe(protocol, writer): - method = "blockchain.scripthash.subscribe" +async def test_scripthash_subscribe(protocol, writer, method): params = [ "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c" ] expect = get_expect(method, params) - data = await protocol.blockchain_scripthash_subscribe( - writer, {"params": params}) + data = await protocol.scripthash_subscribe(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_scripthash_unsubscribe(protocol, writer): +async def test_scripthash_unsubscribe(protocol, writer, method): # Here blockstream doesn't even care params = [ "92dd1eb7c042956d3dd9185a58a2578f61fee91347196604540838ccd0f8c08c" ] - data = await protocol.blockchain_scripthash_unsubscribe( - writer, {"params": params}) + data = await protocol.scripthash_unsubscribe(writer, {"params": params}) assert data["result"] is True -async def test_blockchain_transaction_get(protocol, writer): - method = "blockchain.transaction.get" +async def test_transaction_get(protocol, writer, method): params = [ "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20" ] expect = get_expect(method, params) - data = await protocol.blockchain_transaction_get(writer, {"params": params}) + data = await protocol.transaction_get(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_transaction_get_merkle(protocol, writer): - method = "blockchain.transaction.get_merkle" +async def test_transaction_get_merkle(protocol, writer, method): params = [ "a9c3c22cc2589284288b28e802ea81723d649210d59dfa7e03af00475f4cec20", 1970700, ] expect = get_expect(method, params) - data = await protocol.blockchain_transaction_get_merkle( - writer, {"params": params}) + data = await protocol.transaction_get_merkle(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_blockchain_transaction_id_from_pos(protocol, writer): - method = "blockchain.transaction.id_from_pos" +async def test_transaction_id_from_pos(protocol, writer, method): params = [1970700, 28] expect = get_expect(method, params) - data = await protocol.blockchain_transaction_id_from_pos( - writer, {"params": params}) + data = await protocol.transaction_id_from_pos(writer, {"params": params}) assert_equal(data["result"], expect["result"]) params = [1970700, 28, True] expect = get_expect(method, params) - data = await protocol.blockchain_transaction_id_from_pos( - writer, {"params": params}) + data = await protocol.transaction_id_from_pos(writer, {"params": params}) assert_equal(data["result"], expect["result"]) -async def test_server_ping(protocol, writer): - method = "server.ping" +async def test_ping(protocol, writer, method): params = [] expect = get_expect(method, params) - data = await protocol.server_ping(writer, {"params": params}) + data = await protocol.ping(writer, {"params": params}) assert_equal(data["result"], expect["result"]) @@ -247,40 +225,28 @@ class MockWriter(asyncio.StreamWriter): # 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, + "blockchain.block.header": test_block_header, + "blockchain.block.headers": test_block_headers, + # "blockchain.estimatefee": test_estimatefee, + "blockchain.headers.subscribe": test_headers_subscribe, + # "blockchain.relayfee": test_relayfee, + "blockchain.scripthash.get_balance": test_scripthash_get_balance, + "blockchain.scripthash.get_history": test_scripthash_get_history, + # "blockchain.scripthash.get_mempool": test_scripthash_get_mempool, + "blockchain.scripthash.listunspent": test_scripthash_listunspent, + "blockchain.scripthash.subscribe": test_scripthash_subscribe, + "blockchain.scripthash.unsubscribe": test_scripthash_unsubscribe, + # "blockchain.transaction.broadcast": test_transaction_broadcast, + "blockchain.transaction.get": test_transaction_get, + "blockchain.transaction.get_merkle": test_transaction_get_merkle, + "blockchain.transaction.id_from_pos": test_transaction_id_from_pos, + # "mempool.get_fee_histogram": test_get_fee_histogram, + # "server.add_peer": test_add_peer, + # "server.donation_address": test_donation_address, + # "server.features": test_server_features, + # "server.peers_subscribe": test_peers_subscribe, + "server.ping": test_ping, + # "server.version": test_server_version, } @@ -298,7 +264,7 @@ async def main(): for func in orchestration: try: - await orchestration[func](protocol, writer) + await orchestration[func](protocol, writer, func) print(f"PASS: {func}") test_pass.append(func) except AssertionError: