From d2560d260c3e2bc22b0d92f998ba17614c3d3e1a Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Fri, 21 Feb 2020 00:04:37 -0300 Subject: [PATCH 1/4] use stream controller and documented errors --- lbry/error/README.md | 4 +++ lbry/error/__init__.py | 28 +++++++++++++++++++ lbry/wallet/usage_payment.py | 16 +++++++---- .../blockchain/test_wallet_server_sessions.py | 27 ++++++------------ 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/lbry/error/README.md b/lbry/error/README.md index ecb59d7d3..e521671f9 100644 --- a/lbry/error/README.md +++ b/lbry/error/README.md @@ -78,3 +78,7 @@ Code | Name | Message 701 | InvalidExchangeRateResponse | Failed to get exchange rate from {source}: {reason} 702 | CurrencyConversion | {message} 703 | InvalidCurrency | Invalid currency: {currency} is not a supported currency. +**8xx** | WalletServerPayment | **Wallet Server Payment** +801 | InvalidAddressForServerPayment | Invalid address from wallet server: '{address}' - skipping payment round. +802 | WalletLockedDuringServerPayment | Cannot spend funds with locked wallet, skipping payment round. +803 | ServerFeeHigherThanAllowedServerPayment | Server asked {daily_fee} LBC as daily fee, but maximum allowed is {max_fee} LBC. Skipping payment round. diff --git a/lbry/error/__init__.py b/lbry/error/__init__.py index 78fa339da..513f87246 100644 --- a/lbry/error/__init__.py +++ b/lbry/error/__init__.py @@ -377,3 +377,31 @@ class InvalidCurrencyError(CurrencyExchangeError): def __init__(self, currency): self.currency = currency super().__init__(f"Invalid currency: {currency} is not a supported currency.") + + +class WalletServerPaymentError(BaseError): + """ + **Wallet Server Payment** + """ + + +class InvalidAddressForServerPaymentError(WalletServerPaymentError): + + def __init__(self, address): + self.address = address + super().__init__(f"Invalid address from wallet server: '{address}' - skipping payment round.") + + +class WalletLockedDuringServerPaymentError(WalletServerPaymentError): + + def __init__(self): + super().__init__("Cannot spend funds with locked wallet, skipping payment round.") + + +class ServerFeeHigherThanAllowedServerPaymentError(WalletServerPaymentError): + + def __init__(self, daily_fee, max_fee): + self.daily_fee = daily_fee + self.max_fee = max_fee + super().__init__(f"Server asked {daily_fee} LBC as daily fee," + f" but maximum allowed is {max_fee} LBC. Skipping payment round.") diff --git a/lbry/wallet/usage_payment.py b/lbry/wallet/usage_payment.py index 167f10ed6..ac46fe179 100644 --- a/lbry/wallet/usage_payment.py +++ b/lbry/wallet/usage_payment.py @@ -1,7 +1,10 @@ import asyncio import logging +from lbry.error import InvalidAddressForServerPaymentError, WalletLockedDuringServerPaymentError, \ + ServerFeeHigherThanAllowedServerPaymentError from lbry.wallet.dewies import lbc_to_dewies +from lbry.wallet.stream import StreamController from lbry.wallet.transaction import Output, Transaction log = logging.getLogger(__name__) @@ -16,6 +19,9 @@ class WalletServerPayer: self.payment_period = payment_period self.analytics_manager = analytics_manager self.max_fee = max_fee + self._on_payment_controller = StreamController() + self.on_payment = self._on_payment_controller.stream + self.on_payment.listen(None, on_error=lambda e: logging.warning(e.args[0])) async def pay(self): while self.running: @@ -27,18 +33,17 @@ class WalletServerPayer: continue if not self.ledger.is_valid_address(address): - log.warning("Invalid address from wallet server: '%s' - skipping payment round.", address) + self._on_payment_controller.add_error(InvalidAddressForServerPaymentError(address)) continue if self.wallet.is_locked: - log.warning("Cannot spend funds with locked wallet, skipping payment round.") + self._on_payment_controller.add_error(WalletLockedDuringServerPaymentError()) continue amount = lbc_to_dewies(features['daily_fee']) # check that this is in lbc and not dewies limit = lbc_to_dewies(self.max_fee) if amount > limit: - log.warning( - "Server asked %s LBC as daily fee, but maximum allowed is %s LBC. Skipping payment round.", - features['daily_fee'], self.max_fee + self._on_payment_controller.add_error( + ServerFeeHigherThanAllowedServerPaymentError(features['daily_fee'], self.max_fee) ) continue @@ -52,6 +57,7 @@ class WalletServerPayer: await self.ledger.broadcast(tx) if self.analytics_manager: await self.analytics_manager.send_credits_sent() + self._on_payment_controller.add(tx) async def start(self, ledger=None, wallet=None): self.ledger = ledger diff --git a/tests/integration/blockchain/test_wallet_server_sessions.py b/tests/integration/blockchain/test_wallet_server_sessions.py index 1dbb7735f..9344a702d 100644 --- a/tests/integration/blockchain/test_wallet_server_sessions.py +++ b/tests/integration/blockchain/test_wallet_server_sessions.py @@ -2,10 +2,10 @@ import asyncio import lbry import lbry.wallet +from lbry.error import ServerFeeHigherThanAllowedServerPaymentError from lbry.wallet.network import ClientSession -from lbry.testcase import IntegrationTestCase, CommandTestCase, AdvanceTimeTestCase +from lbry.testcase import IntegrationTestCase, CommandTestCase from lbry.wallet.orchstr8.node import SPVNode -from lbry.wallet.usage_payment import WalletServerPayer class TestSessions(IntegrationTestCase): @@ -67,23 +67,14 @@ class TestUsagePayment(CommandTestCase): node = SPVNode(self.conductor.spv_module, node_number=2) await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1.1"}) self.daemon.jsonrpc_settings_set('lbryum_servers', [f"{node.hostname}:{node.port}"]) - with self.assertLogs(level='WARNING') as cm: - await self.daemon.jsonrpc_wallet_reconnect() + on_error = wallet_pay_service.on_payment.where(lambda e: isinstance(e, ServerFeeHigherThanAllowedServerPaymentError)) + await self.daemon.jsonrpc_wallet_reconnect() - features = await self.ledger.network.get_server_features() - self.assertEqual(features["payment_address"], address) - self.assertEqual(features["daily_fee"], "1.1") - elapsed = 0 - while not cm.output: - await asyncio.sleep(0.1) - elapsed += 1 - if elapsed > 30: - raise TimeoutError('Nothing logged for 3 seconds.') - self.assertEqual( - cm.output, - ['WARNING:lbry.wallet.usage_payment:Server asked 1.1 LBC as daily fee, but ' - 'maximum allowed is 1.0 LBC. Skipping payment round.'] - ) + features = await self.ledger.network.get_server_features() + self.assertEqual(features["payment_address"], address) + self.assertEqual(features["daily_fee"], "1.1") + with self.assertRaises(ServerFeeHigherThanAllowedServerPaymentError): + await asyncio.wait_for(on_error, timeout=3) await node.stop(False) await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1.0"}) self.addCleanup(node.stop) From b1318a9958abfd886822146460be1f7fb9c24ae6 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Fri, 21 Feb 2020 10:05:46 -0500 Subject: [PATCH 2/4] minor cleanup --- lbry/error/README.md | 7 ++- lbry/error/__init__.py | 49 ++++++++----------- lbry/wallet/usage_payment.py | 14 ++++-- .../blockchain/test_wallet_server_sessions.py | 7 ++- 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/lbry/error/README.md b/lbry/error/README.md index e521671f9..0d744c2e1 100644 --- a/lbry/error/README.md +++ b/lbry/error/README.md @@ -57,6 +57,9 @@ Code | Name | Message 420 | KeyFeeAboveMaxAllowed | {message} 421 | InvalidPassword | Password is invalid. 422 | IncompatibleWalletServer | '{server}:{port}' has an incompatibly old version. +431 | ServerPaymentInvalidAddress | Invalid address from wallet server: '{address}' - skipping payment round. +432 | ServerPaymentWalletLocked | Cannot spend funds with locked wallet, skipping payment round. +433 | ServerPaymentFeeAboveMaxAllowed | Server asked {daily_fee} LBC as daily fee, but maximum allowed is {max_fee} LBC. Skipping payment round. **5xx** | Blob | **Blobs** 500 | BlobNotFound | Blob not found. 501 | BlobPermissionDenied | Permission denied to read blob. @@ -78,7 +81,3 @@ Code | Name | Message 701 | InvalidExchangeRateResponse | Failed to get exchange rate from {source}: {reason} 702 | CurrencyConversion | {message} 703 | InvalidCurrency | Invalid currency: {currency} is not a supported currency. -**8xx** | WalletServerPayment | **Wallet Server Payment** -801 | InvalidAddressForServerPayment | Invalid address from wallet server: '{address}' - skipping payment round. -802 | WalletLockedDuringServerPayment | Cannot spend funds with locked wallet, skipping payment round. -803 | ServerFeeHigherThanAllowedServerPayment | Server asked {daily_fee} LBC as daily fee, but maximum allowed is {max_fee} LBC. Skipping payment round. diff --git a/lbry/error/__init__.py b/lbry/error/__init__.py index 513f87246..fc49319e7 100644 --- a/lbry/error/__init__.py +++ b/lbry/error/__init__.py @@ -242,6 +242,27 @@ class IncompatibleWalletServerError(WalletError): super().__init__(f"'{server}:{port}' has an incompatibly old version.") +class ServerPaymentInvalidAddressError(WalletError): + + def __init__(self, address): + self.address = address + super().__init__(f"Invalid address from wallet server: '{address}' - skipping payment round.") + + +class ServerPaymentWalletLockedError(WalletError): + + def __init__(self): + super().__init__("Cannot spend funds with locked wallet, skipping payment round.") + + +class ServerPaymentFeeAboveMaxAllowedError(WalletError): + + def __init__(self, daily_fee, max_fee): + self.daily_fee = daily_fee + self.max_fee = max_fee + super().__init__(f"Server asked {daily_fee} LBC as daily fee, but maximum allowed is {max_fee} LBC. Skipping payment round.") + + class BlobError(BaseError): """ **Blobs** @@ -377,31 +398,3 @@ class InvalidCurrencyError(CurrencyExchangeError): def __init__(self, currency): self.currency = currency super().__init__(f"Invalid currency: {currency} is not a supported currency.") - - -class WalletServerPaymentError(BaseError): - """ - **Wallet Server Payment** - """ - - -class InvalidAddressForServerPaymentError(WalletServerPaymentError): - - def __init__(self, address): - self.address = address - super().__init__(f"Invalid address from wallet server: '{address}' - skipping payment round.") - - -class WalletLockedDuringServerPaymentError(WalletServerPaymentError): - - def __init__(self): - super().__init__("Cannot spend funds with locked wallet, skipping payment round.") - - -class ServerFeeHigherThanAllowedServerPaymentError(WalletServerPaymentError): - - def __init__(self, daily_fee, max_fee): - self.daily_fee = daily_fee - self.max_fee = max_fee - super().__init__(f"Server asked {daily_fee} LBC as daily fee," - f" but maximum allowed is {max_fee} LBC. Skipping payment round.") diff --git a/lbry/wallet/usage_payment.py b/lbry/wallet/usage_payment.py index ac46fe179..8195535db 100644 --- a/lbry/wallet/usage_payment.py +++ b/lbry/wallet/usage_payment.py @@ -1,8 +1,11 @@ import asyncio import logging -from lbry.error import InvalidAddressForServerPaymentError, WalletLockedDuringServerPaymentError, \ - ServerFeeHigherThanAllowedServerPaymentError +from lbry.error import ( + ServerPaymentFeeAboveMaxAllowedError, + ServerPaymentInvalidAddressError, + ServerPaymentWalletLockedError +) from lbry.wallet.dewies import lbc_to_dewies from lbry.wallet.stream import StreamController from lbry.wallet.transaction import Output, Transaction @@ -33,17 +36,18 @@ class WalletServerPayer: continue if not self.ledger.is_valid_address(address): - self._on_payment_controller.add_error(InvalidAddressForServerPaymentError(address)) + self._on_payment_controller.add_error(ServerPaymentInvalidAddressError(address)) continue + if self.wallet.is_locked: - self._on_payment_controller.add_error(WalletLockedDuringServerPaymentError()) + self._on_payment_controller.add_error(ServerPaymentWalletLockedError()) continue amount = lbc_to_dewies(features['daily_fee']) # check that this is in lbc and not dewies limit = lbc_to_dewies(self.max_fee) if amount > limit: self._on_payment_controller.add_error( - ServerFeeHigherThanAllowedServerPaymentError(features['daily_fee'], self.max_fee) + ServerPaymentFeeAboveMaxAllowedError(features['daily_fee'], self.max_fee) ) continue diff --git a/tests/integration/blockchain/test_wallet_server_sessions.py b/tests/integration/blockchain/test_wallet_server_sessions.py index 9344a702d..01b43ccac 100644 --- a/tests/integration/blockchain/test_wallet_server_sessions.py +++ b/tests/integration/blockchain/test_wallet_server_sessions.py @@ -2,7 +2,7 @@ import asyncio import lbry import lbry.wallet -from lbry.error import ServerFeeHigherThanAllowedServerPaymentError +from lbry.error import ServerPaymentFeeAboveMaxAllowedError from lbry.wallet.network import ClientSession from lbry.testcase import IntegrationTestCase, CommandTestCase from lbry.wallet.orchstr8.node import SPVNode @@ -67,14 +67,13 @@ class TestUsagePayment(CommandTestCase): node = SPVNode(self.conductor.spv_module, node_number=2) await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1.1"}) self.daemon.jsonrpc_settings_set('lbryum_servers', [f"{node.hostname}:{node.port}"]) - on_error = wallet_pay_service.on_payment.where(lambda e: isinstance(e, ServerFeeHigherThanAllowedServerPaymentError)) await self.daemon.jsonrpc_wallet_reconnect() features = await self.ledger.network.get_server_features() self.assertEqual(features["payment_address"], address) self.assertEqual(features["daily_fee"], "1.1") - with self.assertRaises(ServerFeeHigherThanAllowedServerPaymentError): - await asyncio.wait_for(on_error, timeout=3) + with self.assertRaises(ServerPaymentFeeAboveMaxAllowedError): + await asyncio.wait_for(wallet_pay_service.on_payment.first, timeout=3) await node.stop(False) await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1.0"}) self.addCleanup(node.stop) From c7d42f00c6f91ee0ab97fd22b8881a579143a8e7 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Fri, 21 Feb 2020 10:16:56 -0500 Subject: [PATCH 3/4] shorten error message for ServerPaymentFeeAboveMaxAllowed --- lbry/error/README.md | 2 +- lbry/error/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lbry/error/README.md b/lbry/error/README.md index 0d744c2e1..4af499334 100644 --- a/lbry/error/README.md +++ b/lbry/error/README.md @@ -59,7 +59,7 @@ Code | Name | Message 422 | IncompatibleWalletServer | '{server}:{port}' has an incompatibly old version. 431 | ServerPaymentInvalidAddress | Invalid address from wallet server: '{address}' - skipping payment round. 432 | ServerPaymentWalletLocked | Cannot spend funds with locked wallet, skipping payment round. -433 | ServerPaymentFeeAboveMaxAllowed | Server asked {daily_fee} LBC as daily fee, but maximum allowed is {max_fee} LBC. Skipping payment round. +433 | ServerPaymentFeeAboveMaxAllowed | Daily server fee of {daily_fee} exceeds maximum configured of {max_fee} LBC. **5xx** | Blob | **Blobs** 500 | BlobNotFound | Blob not found. 501 | BlobPermissionDenied | Permission denied to read blob. diff --git a/lbry/error/__init__.py b/lbry/error/__init__.py index fc49319e7..8827f5e17 100644 --- a/lbry/error/__init__.py +++ b/lbry/error/__init__.py @@ -260,7 +260,7 @@ class ServerPaymentFeeAboveMaxAllowedError(WalletError): def __init__(self, daily_fee, max_fee): self.daily_fee = daily_fee self.max_fee = max_fee - super().__init__(f"Server asked {daily_fee} LBC as daily fee, but maximum allowed is {max_fee} LBC. Skipping payment round.") + super().__init__(f"Daily server fee of {daily_fee} exceeds maximum configured of {max_fee} LBC.") class BlobError(BaseError): From 5936444f3e75a1c5a485ba397da9ad94be9d2149 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Fri, 21 Feb 2020 12:18:44 -0500 Subject: [PATCH 4/4] more reliable wait for wallet server payment test --- .../integration/blockchain/test_wallet_server_sessions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/integration/blockchain/test_wallet_server_sessions.py b/tests/integration/blockchain/test_wallet_server_sessions.py index 01b43ccac..df52b446f 100644 --- a/tests/integration/blockchain/test_wallet_server_sessions.py +++ b/tests/integration/blockchain/test_wallet_server_sessions.py @@ -66,6 +66,7 @@ class TestUsagePayment(CommandTestCase): node = SPVNode(self.conductor.spv_module, node_number=2) await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1.1"}) + self.addCleanup(node.stop) self.daemon.jsonrpc_settings_set('lbryum_servers', [f"{node.hostname}:{node.port}"]) await self.daemon.jsonrpc_wallet_reconnect() @@ -74,15 +75,18 @@ class TestUsagePayment(CommandTestCase): self.assertEqual(features["daily_fee"], "1.1") with self.assertRaises(ServerPaymentFeeAboveMaxAllowedError): await asyncio.wait_for(wallet_pay_service.on_payment.first, timeout=3) + await node.stop(False) await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1.0"}) - self.addCleanup(node.stop) self.daemon.jsonrpc_settings_set('lbryum_servers', [f"{node.hostname}:{node.port}"]) await self.daemon.jsonrpc_wallet_reconnect() features = await self.ledger.network.get_server_features() self.assertEqual(features["payment_address"], address) self.assertEqual(features["daily_fee"], "1.0") - await asyncio.wait_for(self.on_address_update(address), timeout=1) + await asyncio.wait([ + wallet_pay_service.on_payment.first, + self.on_address_update(address) + ], timeout=3) _, history = await self.ledger.get_local_status_and_history(address) txid, nout = history[0] tx_details = await self.daemon.jsonrpc_transaction_show(txid)