From 0b0f6d48d20c2a823bdfa0ee33262985d4242cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sun, 25 Feb 2024 10:20:43 +0100 Subject: [PATCH] feat: remove mempool.space dependency (#24) --- boltz_client/boltz.py | 67 +++---- boltz_client/cli.py | 15 +- boltz_client/mempool.py | 189 -------------------- boltz_client/onchain.py | 56 +++--- boltz_client/onchain_wally.py | 19 +- docker/docker-compose.yml | 95 ---------- docker/regtest | 2 - tests/conftest.py | 10 +- tests/test_boltz.py | 1 - tests/test_boltz_lifecycle_reverse_swap.py | 16 +- tests/test_boltz_lifecycle_swap.py | 31 +--- tests/test_liquid_lifecycle_reverse_swap.py | 8 +- tests/test_liquid_lifecycle_swap.py | 16 +- tests/test_mempool.py | 47 ----- 14 files changed, 90 insertions(+), 482 deletions(-) delete mode 100644 boltz_client/mempool.py delete mode 100644 tests/test_mempool.py diff --git a/boltz_client/boltz.py b/boltz_client/boltz.py index 5303555..24a638e 100644 --- a/boltz_client/boltz.py +++ b/boltz_client/boltz.py @@ -9,13 +9,11 @@ import httpx from .helpers import req_wrap -from .mempool import MempoolClient from .onchain import ( create_claim_tx, create_key_pair, create_preimage, create_refund_tx, - get_txid, validate_address, ) @@ -58,7 +56,9 @@ def __init__(self, message: str): @dataclass class BoltzSwapTransactionResponse: + transactionId: Optional[str] = None transactionHex: Optional[str] = None + timeoutEta: Optional[str] = None timeoutBlockHeight: Optional[str] = None failureReason: Optional[str] = None @@ -100,8 +100,6 @@ class BoltzConfig: network_liquid: str = "liquidv1" pairs: list = field(default_factory=lambda: ["BTC/BTC", "L-BTC/BTC"]) api_url: str = "https://boltz.exchange/api" - mempool_url: str = "https://mempool.space/api" - mempool_liquid_url: str = "https://liquid.network/api" referral_id: str = "dni" @@ -119,12 +117,8 @@ def __init__(self, config: BoltzConfig, pair: str = "BTC/BTC"): if self.pair == "L-BTC/BTC": self.network = self._cfg.network_liquid - mempool_url = self._cfg.mempool_liquid_url else: self.network = self._cfg.network - mempool_url = self._cfg.mempool_url - - self.mempool = MempoolClient(mempool_url) def request(self, funcname, *args, **kwargs) -> dict: try: @@ -171,13 +165,6 @@ def get_fee_estimation_claim(self) -> int: def get_fee_estimation_refund(self) -> int: return self.fees["minerFees"]["baseAsset"]["normal"] - def get_fee_estimation(self, feerate: Optional[int]) -> int: - # TODO: hardcoded maximum tx size, in the future we try to get the size of the - # tx via embit we need a function like Transaction.vsize() - tx_size_vbyte = 200 - mempool_fees = feerate if feerate else self.mempool.get_fees() - return mempool_fees * tx_size_vbyte - def get_pairs(self) -> dict: data = self.request( "get", @@ -223,24 +210,25 @@ def swap_transaction(self, boltz_id: str) -> BoltzSwapTransactionResponse: return res - async def wait_for_txid(self, boltz_id: str) -> str: + async def wait_for_tx(self, boltz_id: str) -> str: while True: try: swap_transaction = self.swap_transaction(boltz_id) - if swap_transaction.transactionHex: - return get_txid(swap_transaction.transactionHex, self.pair) - raise ValueError("transactionHex is empty") + assert swap_transaction.transactionHex + return swap_transaction.transactionHex except (ValueError, BoltzApiException, BoltzSwapTransactionException): await asyncio.sleep(3) - async def wait_for_txid_on_status(self, boltz_id: str) -> str: + async def wait_for_tx_on_status(self, boltz_id: str, zeroconf: bool = True) -> str: while True: try: status = self.swap_status(boltz_id) assert status.transaction - txid = status.transaction.get("id") - assert txid - return txid + txHex = status.transaction.get("hex") + assert txHex + if not zeroconf: + assert status.status == "transaction.confirmed" + return txHex except (BoltzApiException, BoltzSwapStatusException, AssertionError): await asyncio.sleep(3) @@ -259,29 +247,22 @@ async def claim_reverse_swap( preimage_hex: str, redeem_script_hex: str, zeroconf: bool = True, - feerate: Optional[int] = None, blinding_key: Optional[str] = None, ): - self.validate_address(receive_address) - _lockup_address = self.validate_address(lockup_address) - lockup_txid = await self.wait_for_txid_on_status(boltz_id) - lockup_tx = await self.mempool.get_tx_from_txid(lockup_txid, _lockup_address) - - if not zeroconf and lockup_tx.status != "confirmed": - await self.mempool.wait_for_tx_confirmed(lockup_tx.txid) + self.validate_address(lockup_address) + lockup_rawtx = await self.wait_for_tx_on_status(boltz_id, zeroconf) transaction = create_claim_tx( - lockup_tx=lockup_tx, + lockup_address=lockup_address, + lockup_rawtx=lockup_rawtx, receive_address=receive_address, privkey_wif=privkey_wif, redeem_script_hex=redeem_script_hex, preimage_hex=preimage_hex, pair=self.pair, blinding_key=blinding_key, - fees=self.get_fee_estimation(feerate) - if feerate - else self.get_fee_estimation_claim(), + fees=self.get_fee_estimation_claim(), ) return self.send_onchain_tx(transaction) @@ -293,25 +274,23 @@ async def refund_swap( receive_address: str, redeem_script_hex: str, timeout_block_height: int, - feerate: Optional[int] = None, blinding_key: Optional[str] = None, ) -> str: - self.mempool.check_block_height(timeout_block_height) + # self.mempool.check_block_height(timeout_block_height) self.validate_address(receive_address) - _lockup_address = self.validate_address(lockup_address) - lockup_txid = await self.wait_for_txid(boltz_id) - lockup_tx = await self.mempool.get_tx_from_txid(lockup_txid, _lockup_address) + self.validate_address(lockup_address) + + lockup_rawtx = await self.wait_for_tx(boltz_id) transaction = create_refund_tx( - lockup_tx=lockup_tx, + lockup_address=lockup_address, + lockup_rawtx=lockup_rawtx, privkey_wif=privkey_wif, receive_address=receive_address, redeem_script_hex=redeem_script_hex, timeout_block_height=timeout_block_height, pair=self.pair, blinding_key=blinding_key, - fees=self.get_fee_estimation(feerate) - if feerate - else self.get_fee_estimation_refund(), + fees=self.get_fee_estimation_refund(), ) return self.send_onchain_tx(transaction) diff --git a/boltz_client/cli.py b/boltz_client/cli.py index 979811e..d85ec5f 100644 --- a/boltz_client/cli.py +++ b/boltz_client/cli.py @@ -21,8 +21,6 @@ # network="regtest", # network_liquid="elementsregtest", # api_url="http://localhost:9001", -# mempool_url="http://localhost:8999/api/v1", -# mempool_liquid_url="http://localhost:8998/api/v1", # ) @@ -30,7 +28,7 @@ def command_group(): """ Python CLI of boltz-client-python, enjoy submarine swapping. :) - Uses mempool.space for retrieving onchain data""" + """ @click.command() @@ -294,21 +292,10 @@ def show_pairs(): click.echo(json.dumps(data)) -@click.command() -def get_fees(): - """ - show mempool recommended fees - """ - client = BoltzClient(config) - fees = client.mempool.get_fees() - click.echo(fees) - - def main(): """main function""" command_group.add_command(swap_status) command_group.add_command(show_pairs) - command_group.add_command(get_fees) command_group.add_command(create_swap) command_group.add_command(refund_swap) command_group.add_command(create_reverse_swap) diff --git a/boltz_client/mempool.py b/boltz_client/mempool.py deleted file mode 100644 index fe9637e..0000000 --- a/boltz_client/mempool.py +++ /dev/null @@ -1,189 +0,0 @@ -""" boltz_client mempool module """ - -import asyncio -import json -from dataclasses import dataclass -from typing import Optional - -import httpx -from websockets.client import connect -from websockets.exceptions import ConnectionClosed - -from .helpers import req_wrap - - -@dataclass -class LockupData: - status: str - tx_hex: str - txid: str - script_pub_key: str - vout_cnt: int - vout_amount: int - - -class MempoolApiException(Exception): - pass - - -class MempoolBlockHeightException(Exception): - pass - - -class MempoolClient: - def __init__(self, url): - self._api_url = url - ws_url = url.replace("https", "wss") - ws_url = url.replace("http", "ws") - ws_url += "/ws" - self._ws_url = ws_url - # just check if mempool is available - self.get_blockheight() - - def request(self, funcname, *args, **kwargs) -> dict: - try: - return req_wrap(funcname, *args, **kwargs) - except httpx.RequestError as exc: - msg = f"unreachable: {exc.request.url!r}." - raise MempoolApiException(f"mempool api connection error: {msg}") from exc - except httpx.HTTPStatusError as exc: - msg = ( - f"{exc.response.status_code} while requesting " - f"{exc.request.url!r}. message: {exc.response.text}" - ) - raise MempoolApiException(f"mempool api status error: {msg}") from exc - - async def wait_for_websocket_message(self, send, message_key): - async for websocket in connect(self._ws_url): - try: - await websocket.send(json.dumps({"action": "want", "data": ["blocks"]})) - await websocket.send(json.dumps(send)) - async for raw in websocket: - message = json.loads(raw) - if message_key in message: - return message.get(message_key) - except ConnectionClosed: - continue - - async def wait_for_one_websocket_message(self, send): - async with connect(self._ws_url) as websocket: - await websocket.send( - json.dumps({"action": "want", "data": ["blocks", "mempool-blocks"]}) - ) - await websocket.send(json.dumps(send)) - raw = await asyncio.wait_for(websocket.recv(), timeout=10) - return json.loads(raw) if raw else None - - async def wait_for_tx_confirmed(self, txid: str): - return await self.wait_for_websocket_message({"track-tx": txid}, "txConfirmed") - - async def wait_for_lockup_tx(self, address: str) -> LockupData: - message = await self.wait_for_websocket_message( - {"track-address": address}, "address-transactions" - ) - if not message: - # restart - return await self.wait_for_lockup_tx(address) - lockup_tx = self.find_tx_and_output(message, address) - if not lockup_tx: - # restart - return await self.wait_for_lockup_tx(address) - return lockup_tx - - def find_tx_and_output(self, txs, address: str) -> Optional[LockupData]: - if len(txs) == 0: - return None - for tx in txs: - output = self.find_output(tx, address) - if output: - return output - return None - - def find_output(self, tx, address: str) -> Optional[LockupData]: - for i, vout in enumerate(tx["vout"]): - if vout["scriptpubkey_address"] == address: - status = "confirmed" if tx["status"]["confirmed"] else "unconfirmed" - return LockupData( - tx_hex=self.get_tx_hex(tx["txid"]), - txid=tx["txid"], - script_pub_key=vout["scriptpubkey_address"], - vout_cnt=i, - vout_amount=vout.get("value") or 0, - status=status, - ) - return None - - def get_tx(self, txid: str): - return self.request( - "get", - f"{self._api_url}/tx/{txid}", - headers={"Content-Type": "application/json"}, - ) - - def get_tx_hex(self, txid: str) -> str: - return self.request( - "get", - f"{self._api_url}/tx/{txid}/hex", - headers={"Content-Type": "text/plain"}, - )["text"] - - def get_txs_from_address(self, address: str): - return self.request( - "get", - f"{self._api_url}/address/{address}/txs", - headers={"Content-Type": "application/json"}, - ) - - async def get_tx_from_txid(self, txid: str, address: str) -> LockupData: - while True: - try: - tx = self.get_tx(txid) - output = self.find_output(tx, address) - if output: - return output - except MempoolApiException: - pass - await asyncio.sleep(3) - - async def get_tx_from_address(self, address: str) -> LockupData: - txs = self.get_txs_from_address(address) - if len(txs) == 0: - return await self.wait_for_lockup_tx(address) - lockup_tx = self.find_tx_and_output(txs, address) - if not lockup_tx: - return await self.wait_for_lockup_tx(address) - return lockup_tx - - def get_fees(self) -> int: - # mempool.space quirk, needed for regtest - api_url = self._api_url.replace("/v1", "") - data = self.request( - "get", - f"{api_url}/v1/fees/recommended", - headers={"Content-Type": "application/json"}, - ) - return int(data["halfHourFee"]) - - def get_blockheight(self) -> int: - data = self.request( - "get", - f"{self._api_url}/blocks/tip/height", - headers={"Content-Type": "text/plain"}, - ) - return int(data["text"]) - - def check_block_height(self, timeout_block_height: int) -> None: - current_block_height = self.get_blockheight() - if current_block_height < timeout_block_height: - raise MempoolBlockHeightException( - f"current_block_height ({current_block_height}) " - f"has not yet exceeded ({timeout_block_height})" - ) - - def send_onchain_tx(self, tx_hex: str): - return self.request( - "post", - f"{self._api_url}/tx", - headers={"Content-Type": "text/plain"}, - content=tx_hex, - ) diff --git a/boltz_client/onchain.py b/boltz_client/onchain.py index be6dabd..d4f0df3 100644 --- a/boltz_client/onchain.py +++ b/boltz_client/onchain.py @@ -7,25 +7,12 @@ from embit.base import EmbitError from embit.liquid.addresses import to_unconfidential from embit.liquid.networks import NETWORKS as LNETWORKS -from embit.liquid.transaction import LTransaction from embit.networks import NETWORKS from embit.transaction import SIGHASH, Transaction, TransactionInput, TransactionOutput -from .mempool import LockupData from .onchain_wally import create_liquid_tx -def get_txid(tx_hex: str, pair: str = "BTC/BTC") -> str: - try: - if pair == "L-BTC/BTC": - tx = LTransaction.from_string(tx_hex) - else: - tx = Transaction.from_string(tx_hex) - return tx.txid().hex() - except EmbitError as exc: - raise ValueError("Invalid transaction hex") from exc - - def validate_address(address: str, network: str, pair: str) -> str: if pair == "L-BTC/BTC": net = LNETWORKS[network] @@ -69,7 +56,8 @@ def create_refund_tx( receive_address: str, redeem_script_hex: str, timeout_block_height: int, - lockup_tx: LockupData, + lockup_address: str, + lockup_rawtx: str, pair: str, fees: int, blinding_key: Optional[str] = None, @@ -79,10 +67,11 @@ def create_refund_tx( rs += sha256(bytes.fromhex(redeem_script_hex)).digest() script_sig = rs return create_onchain_tx( + lockup_address=lockup_address, sequence=0xFFFFFFFE, redeem_script_hex=redeem_script_hex, privkey_wif=privkey_wif, - lockup_tx=lockup_tx, + lockup_rawtx=lockup_rawtx, receive_address=receive_address, timeout_block_height=timeout_block_height, script_sig=script_sig, @@ -93,18 +82,20 @@ def create_refund_tx( def create_claim_tx( + lockup_address: str, preimage_hex: str, privkey_wif: str, receive_address: str, redeem_script_hex: str, - lockup_tx: LockupData, + lockup_rawtx: str, fees: int, pair: str, blinding_key: Optional[str] = None, ) -> str: return create_onchain_tx( + lockup_address=lockup_address, preimage_hex=preimage_hex, - lockup_tx=lockup_tx, + lockup_rawtx=lockup_rawtx, receive_address=receive_address, privkey_wif=privkey_wif, redeem_script_hex=redeem_script_hex, @@ -115,7 +106,8 @@ def create_claim_tx( def create_onchain_tx( - lockup_tx: LockupData, + lockup_address: str, + lockup_rawtx: str, receive_address: str, privkey_wif: str, redeem_script_hex: str, @@ -133,7 +125,8 @@ def create_onchain_tx( raise ValueError("Blinding key is required for L-BTC/BTC pair") return create_liquid_tx( - lockup_tx=lockup_tx, + lockup_rawtx=lockup_rawtx, + lockup_address=lockup_address, receive_address=receive_address, privkey_wif=privkey_wif, redeem_script_hex=redeem_script_hex, @@ -144,14 +137,31 @@ def create_onchain_tx( blinding_key=blinding_key, ) + try: + lockup_transaction = Transaction.from_string(lockup_rawtx) + except EmbitError as exc: + raise ValueError("Invalid lockup transaction hex") from exc + + txid = lockup_transaction.txid() + vout_amount: Optional[int] = None + vout_index: int = 0 + for vout in lockup_transaction.vout: + if vout.script_pubkey == script.address_to_scriptpubkey(lockup_address): + vout_amount = vout.value + break + vout_index += 1 + + if vout_amount is None: + raise ValueError("No matching vout found in lockup transaction") + vout = TransactionOutput( - lockup_tx.vout_amount - fees, + vout_amount - fees, script.address_to_scriptpubkey(receive_address), ) vout = [vout] vin = TransactionInput( - bytes.fromhex(lockup_tx.txid), - lockup_tx.vout_cnt, + txid, + vout_index, sequence=sequence, script_sig=script.Script(data=script_sig) if script_sig else None, ) @@ -161,7 +171,7 @@ def create_onchain_tx( tx.locktime = timeout_block_height redeem_script = script.Script(data=bytes.fromhex(redeem_script_hex)) - h = tx.sighash_segwit(0, redeem_script, lockup_tx.vout_amount) + h = tx.sighash_segwit(0, redeem_script, vout_amount) sig = ec.PrivateKey.from_wif(privkey_wif).sign(h).serialize() + bytes([SIGHASH.ALL]) witness_script = script.Witness( items=[sig, bytes.fromhex(preimage_hex), bytes.fromhex(redeem_script_hex)] diff --git a/boltz_client/onchain_wally.py b/boltz_client/onchain_wally.py index 06650de..7eff4ea 100644 --- a/boltz_client/onchain_wally.py +++ b/boltz_client/onchain_wally.py @@ -10,8 +10,6 @@ from dataclasses import dataclass from typing import Any, Optional -from .mempool import LockupData - @dataclass class Network: @@ -147,7 +145,8 @@ def decode_address( def create_liquid_tx( - lockup_tx: LockupData, + lockup_rawtx: str, + lockup_address: str, receive_address: str, privkey_wif: str, redeem_script_hex: str, @@ -184,19 +183,19 @@ def create_liquid_tx( wally, network, receive_address ) + _, lockup_script_pubkey = decode_address(wally, network, lockup_address) + # parse lockup tx lockup_transaction = wally.tx_from_hex( - lockup_tx.tx_hex, wally.WALLY_TX_FLAG_USE_ELEMENTS + lockup_rawtx, wally.WALLY_TX_FLAG_USE_ELEMENTS ) vout_n: Optional[int] = None for vout in range(wally.tx_get_num_outputs(lockup_transaction)): script_out = wally.tx_get_output_script(lockup_transaction, vout) # type: ignore - - # Lockup addresses on liquid are always bech32 - pub_key = wally.addr_segwit_from_bytes(script_out, network.bech32_prefix, 0) - if pub_key == lockup_tx.script_pub_key: - vout_n = vout - break + if script_out: + if script_out == lockup_script_pubkey: + vout_n = vout + break assert vout_n is not None, "Lockup vout not found" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a92a7e8..765ad9b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -99,98 +99,3 @@ services: - 10009 volumes: - ./data/lnd:/root/.lnd/ - - # electrs: - # depends_on: - # - bitcoind - # hostname: electrs - # image: getumbrel/electrs:latest - # environment: - # ELECTRS_ELECTRUM_RPC_ADDR: electrs:50001 - # ELECTRS_DAEMON_RPC_ADDR: bitcoind:18443 - # ELECTRS_DAEMON_P2P_ADDR: bitcoind:18444 - # entrypoint: sh -c 'sleep 15 && electrs' - # ports: - # - 50001:50001 - # volumes: - # - ./data/electrs/:/data/.electrs/ - - # mempool-web: - # restart: on-failure - # depends_on: - # - mempool-api - # environment: - # FRONTEND_HTTP_PORT: 8080 - # BACKEND_MAINNET_HTTP_HOST: mempool-api - # image: mempool/frontend:latest - # ports: - # - 8080:8080 - - mempool-api: - image: mempool/backend:latest - depends_on: - # - electrs - - mempool-db - environment: - REDIS_ENABLED: false - MEMPOOL_BACKEND: none - BACKEND_HTTP_PORT: 8999 - # ELECTRUM_HOST: electrs - # ELECTRUM_PORT: 50001 - # ELECTRUM_TLS_ENABLED: false - CORE_RPC_HOST: bitcoind - CORE_RPC_PORT: 18443 - CORE_RPC_USERNAME: boltz - CORE_RPC_PASSWORD: boltz - DATABASE_ENABLED: true - DATABASE_HOST: mempool-db - DATABASE_DATABASE: mempool - DATABASE_USERNAME: mempool - DATABASE_PASSWORD: mempool - # STATISTICS_ENABLED: true - restart: unless-stopped - ports: - - 8999:8999 - - mempool-db: - image: mariadb:10.5.8 - environment: - MYSQL_DATABASE: mempool - MYSQL_USER: mempool - MYSQL_PASSWORD: mempool - MYSQL_ROOT_PASSWORD: admin - - mempool-api-liquid: - image: mempool/backend:latest - depends_on: - # - electrs - - mempool-db - environment: - REDIS_ENABLED: false - MEMPOOL_NETWORK: liquid - BACKEND_HTTP_PORT: 8998 - MEMPOOL_BACKEND: none - # ELECTRUM_HOST: electrs - # ELECTRUM_PORT: 50001 - # ELECTRUM_TLS_ENABLED: false - CORE_RPC_HOST: elementsd - CORE_RPC_PORT: 18884 - CORE_RPC_USERNAME: boltz - CORE_RPC_PASSWORD: boltz - DATABASE_ENABLED: true - DATABASE_HOST: mempool-db-liquid - DATABASE_DATABASE: mempool - DATABASE_USERNAME: mempool - DATABASE_PASSWORD: mempool - # STATISTICS_ENABLED: true - restart: unless-stopped - ports: - - 8998:8998 - - mempool-db-liquid: - environment: - MYSQL_DATABASE: mempool - MYSQL_USER: mempool - MYSQL_PASSWORD: mempool - MYSQL_ROOT_PASSWORD: admin - image: mariadb:10.5.8 diff --git a/docker/regtest b/docker/regtest index 6b18964..65e547d 100755 --- a/docker/regtest +++ b/docker/regtest @@ -57,8 +57,6 @@ run "cln channel[0].amount_msat" $channel_size_msat $(lightning-cli-sim listfund run "cln channel[0].our_amount_msat" $balance_size_msat $(lightning-cli-sim listfunds | jq -r ".channels[0].our_amount_msat") run "boltz service status" "200" $(curl -s -o /dev/null --head -w "%{http_code}" "http://localhost:9001/version") -run "mempool service status" "200" $(curl -s -o /dev/null --head -w "%{http_code}" "http://localhost:8999/api/v1/blocks/tip/height") -run "mempool_liquid service status" "200" $(curl -s -o /dev/null --head -w "%{http_code}" "http://localhost:8998/api/v1/blocks/tip/height") # return non-zero exit code if a test fails if [[ "$failed" == "true" ]]; then diff --git a/tests/conftest.py b/tests/conftest.py index 4315b51..96b684d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,8 +12,6 @@ network="regtest", network_liquid="elementsregtest", api_url="http://localhost:9001", - mempool_url="http://localhost:8999/api/v1", - mempool_liquid_url="http://localhost:8998/api/v1", ) @@ -53,7 +51,7 @@ async def raw_tx(): @pytest_asyncio.fixture(scope="session") async def pr(): - invoice = get_invoice(10000, "pr-1") + invoice = get_invoice(50000, "pr-1") yield invoice["bolt11"] @@ -65,17 +63,17 @@ async def pr_small(): @pytest_asyncio.fixture(scope="session") async def pr_refund(): - invoice = get_invoice(10001, "pr-3") + invoice = get_invoice(50001, "pr-3") yield invoice["bolt11"] @pytest_asyncio.fixture(scope="session") async def liquid_pr(): - invoice = get_invoice(10000, "liquid-pr-1") + invoice = get_invoice(55555, "liquid-pr-1") yield invoice["bolt11"] @pytest_asyncio.fixture(scope="session") async def liquid_pr_refund(): - invoice = get_invoice(10001, "liquid-pr-2") + invoice = get_invoice(50001, "liquid-pr-2") yield invoice["bolt11"] diff --git a/tests/test_boltz.py b/tests/test_boltz.py index 2ff3424..b8dad0f 100644 --- a/tests/test_boltz.py +++ b/tests/test_boltz.py @@ -14,7 +14,6 @@ async def test_api_exception(): config = BoltzConfig( network="regtest", api_url="http://localhost:9999", - mempool_url="http://localhost:8080", ) with pytest.raises(BoltzApiException): BoltzClient(config) diff --git a/tests/test_boltz_lifecycle_reverse_swap.py b/tests/test_boltz_lifecycle_reverse_swap.py index c5032df..beea395 100644 --- a/tests/test_boltz_lifecycle_reverse_swap.py +++ b/tests/test_boltz_lifecycle_reverse_swap.py @@ -13,7 +13,7 @@ @pytest.mark.asyncio async def test_create_reverse_swap_and_claim(client: BoltzClient): - claim_privkey_wif, preimage_hex, swap = client.create_reverse_swap(10000) + claim_privkey_wif, preimage_hex, swap = client.create_reverse_swap(50000) assert isinstance(claim_privkey_wif, str) assert isinstance(preimage_hex, str) assert isinstance(swap, BoltzReverseSwapResponse) @@ -39,7 +39,8 @@ async def test_create_reverse_swap_and_claim(client: BoltzClient): assert False new_address = create_onchain_address(client.pair) - txid = await client.claim_reverse_swap( + + task = asyncio.create_task(client.claim_reverse_swap( boltz_id=swap.id, receive_address=new_address, lockup_address=swap.lockupAddress, @@ -48,11 +49,13 @@ async def test_create_reverse_swap_and_claim(client: BoltzClient): privkey_wif=claim_privkey_wif, preimage_hex=preimage_hex, zeroconf=True, - ) + )) - task = asyncio.create_task(client.mempool.wait_for_tx_confirmed(txid)) mine_blocks() - await task + + txid = await task + + assert txid, "txid is not None" swap_status_after_confirmed = client.swap_status(swap.id) assert swap_status_after_confirmed.status == "invoice.settled" @@ -84,8 +87,7 @@ async def test_create_reverse_swap_direction(client: BoltzClient): ) mine_blocks() - tx = client.mempool.get_tx(txid) - assert tx["vout"][0]["value"] == amount + assert txid, "txid is not None" # wait for invoice going through p.wait() diff --git a/tests/test_boltz_lifecycle_swap.py b/tests/test_boltz_lifecycle_swap.py index be3c74b..2a761d8 100644 --- a/tests/test_boltz_lifecycle_swap.py +++ b/tests/test_boltz_lifecycle_swap.py @@ -8,7 +8,6 @@ BoltzSwapStatusException, BoltzSwapStatusResponse, ) -from boltz_client.mempool import MempoolBlockHeightException from .helpers import create_onchain_address, mine_blocks, pay_onchain, get_invoice @@ -37,16 +36,17 @@ async def test_create_swap_and_check_status(client, pr): assert hasattr(swap_status, "status") assert swap_status.status == "invoice.set" - task = asyncio.create_task(client.mempool.wait_for_lockup_tx(swap.address)) + task = asyncio.create_task(client.wait_for_tx(swap.id)) txid = pay_onchain(swap.address, swap.expectedAmount) await task + assert txid, "txid is not None" + swap_status_after_payment = client.swap_status(swap.id) assert swap_status_after_payment.status == "transaction.mempool" - task = asyncio.create_task(client.mempool.wait_for_tx_confirmed(txid)) mine_blocks() - await task + await asyncio.sleep(1) swap_status_after_confirmed = client.swap_status(swap.id) assert swap_status_after_confirmed.status == "transaction.claimed" @@ -56,36 +56,23 @@ async def test_create_swap_and_check_status(client, pr): async def test_create_swap_and_refund(client: BoltzClient, pr_refund): refund_privkey_wif, swap = client.create_swap(pr_refund) - task = asyncio.create_task(client.mempool.wait_for_lockup_tx(swap.address)) + task = asyncio.create_task(client.wait_for_tx(swap.id)) # pay to less onchain so the swap fails txid = pay_onchain(swap.address, swap.expectedAmount - 1000) + assert txid, "txid is not None" await task - task = asyncio.create_task(client.mempool.wait_for_tx_confirmed(txid)) mine_blocks() - await task with pytest.raises(BoltzSwapStatusException): client.swap_status(swap.id) onchain_address = create_onchain_address() - # try refund before timeout - with pytest.raises(MempoolBlockHeightException): - await client.refund_swap( - boltz_id=swap.id, - privkey_wif=refund_privkey_wif, - lockup_address=swap.address, - receive_address=onchain_address, - redeem_script_hex=swap.redeemScript, - timeout_block_height=swap.timeoutBlockHeight, - ) - # wait for timeout - blocks_to_mine = swap.timeoutBlockHeight - client.mempool.get_blockheight() + 3 - mine_blocks(blocks=blocks_to_mine) + mine_blocks(blocks=250) await asyncio.sleep(3) @@ -99,10 +86,6 @@ async def test_create_swap_and_refund(client: BoltzClient, pr_refund): timeout_block_height=swap.timeoutBlockHeight, ) - task = asyncio.create_task(client.mempool.wait_for_tx_confirmed(txid)) - mine_blocks() - await task - # check status try: client.swap_status(swap.id) diff --git a/tests/test_liquid_lifecycle_reverse_swap.py b/tests/test_liquid_lifecycle_reverse_swap.py index 24442b5..d8f8e13 100644 --- a/tests/test_liquid_lifecycle_reverse_swap.py +++ b/tests/test_liquid_lifecycle_reverse_swap.py @@ -1,4 +1,4 @@ -import asyncio +# import asyncio import pytest from boltz_client.boltz import BoltzClient @@ -7,7 +7,7 @@ @pytest.mark.asyncio async def test_liquid_create_reverse_swap_and_claim(client_liquid: BoltzClient): - claim_privkey_wif, preimage_hex, swap = client_liquid.create_reverse_swap(10000) + claim_privkey_wif, preimage_hex, swap = client_liquid.create_reverse_swap(50000) new_address = create_onchain_address(client_liquid.pair) # create_task is used because pay_invoice is stuck as long as boltz does not @@ -29,9 +29,7 @@ async def test_liquid_create_reverse_swap_and_claim(client_liquid: BoltzClient): zeroconf=True, ) - task = asyncio.create_task(client_liquid.mempool.wait_for_tx_confirmed(txid)) - mine_blocks(client_liquid.pair) - await task + assert txid, "txid is not None" mine_blocks(client_liquid.pair) diff --git a/tests/test_liquid_lifecycle_swap.py b/tests/test_liquid_lifecycle_swap.py index 9760629..ab85f51 100644 --- a/tests/test_liquid_lifecycle_swap.py +++ b/tests/test_liquid_lifecycle_swap.py @@ -5,7 +5,6 @@ BoltzClient, BoltzSwapStatusException, ) -from boltz_client.mempool import MempoolBlockHeightException from .helpers import create_onchain_address, mine_blocks, pay_onchain @@ -44,21 +43,8 @@ async def test_create_swap_and_refund(client_liquid: BoltzClient, liquid_pr_refu onchain_address = create_onchain_address(client_liquid.pair) - # try refund before timeout - with pytest.raises(MempoolBlockHeightException): - await client_liquid.refund_swap( - boltz_id=swap.id, - privkey_wif=refund_privkey_wif, - lockup_address=swap.address, - receive_address=onchain_address, - redeem_script_hex=swap.redeemScript, - timeout_block_height=swap.timeoutBlockHeight, - blinding_key=swap.blindingKey, - ) - # wait for timeout - blocks_to_mine = swap.timeoutBlockHeight - client_liquid.mempool.get_blockheight() + 10 - mine_blocks(pair=client_liquid.pair, blocks=blocks_to_mine) + mine_blocks(pair=client_liquid.pair, blocks=1000) await asyncio.sleep(10) diff --git a/tests/test_mempool.py b/tests/test_mempool.py deleted file mode 100644 index ddfde92..0000000 --- a/tests/test_mempool.py +++ /dev/null @@ -1,47 +0,0 @@ -import pytest - -from boltz_client.boltz import BoltzClient, BoltzConfig -from boltz_client.mempool import MempoolApiException, MempoolBlockHeightException - - -@pytest.mark.asyncio -async def test_api_exception(): - config = BoltzConfig( - network="regtest", - api_url="http://localhost:9001", - mempool_url="http://localhost:8888", - ) - with pytest.raises(MempoolApiException): - BoltzClient(config) - - -@pytest.mark.asyncio -async def test_blockheight(client): - blockheight = client.mempool.get_blockheight() - assert isinstance(blockheight, int) - - -@pytest.mark.asyncio -async def test_check_blockheight_exception(client): - with pytest.raises(MempoolBlockHeightException): - blockheight = client.mempool.get_blockheight() - client.mempool.check_block_height(blockheight + 1) - - -@pytest.mark.asyncio -async def test_check_blockheight(client): - blockheight = client.mempool.get_blockheight() - client.mempool.check_block_height(blockheight) - - -@pytest.mark.asyncio -async def test_fees(client): - fees = client.mempool.get_fees() - assert isinstance(fees, int) - assert fees == 1 - - -@pytest.mark.asyncio -async def test_send_invalid_onchain(client, raw_tx_invalid): - with pytest.raises(MempoolApiException): - client.mempool.send_onchain_tx(raw_tx_invalid)