diff --git a/lbry/wallet/server/env.py b/lbry/wallet/server/env.py index 857b70803..9a06c8145 100644 --- a/lbry/wallet/server/env.py +++ b/lbry/wallet/server/env.py @@ -72,7 +72,7 @@ class Env: self.tor_proxy_host = self.default('TOR_PROXY_HOST', 'localhost') self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None) # The electrum client takes the empty string as unspecified - self.donation_address = self.default('DONATION_ADDRESS', '') + self.payment_address = self.default('PAYMENT_ADDRESS', '') # Server limits to help prevent DoS self.max_send = self.integer('MAX_SEND', 1000000) self.max_subs = self.integer('MAX_SUBS', 250000) diff --git a/lbry/wallet/server/session.py b/lbry/wallet/server/session.py index dcc05f8e9..e8aca5cc9 100644 --- a/lbry/wallet/server/session.py +++ b/lbry/wallet/server/session.py @@ -854,7 +854,7 @@ class LBRYElectrumX(SessionBase): 'protocol_max': max_str, 'genesis_hash': env.coin.GENESIS_HASH, 'description': env.description, - 'payment_address': env.donation_address, + 'payment_address': env.payment_address, 'daily_fee': env.daily_fee, 'hash_function': 'sha256', } @@ -1362,14 +1362,14 @@ class LBRYElectrumX(SessionBase): ('$SERVER_VERSION', self.version), ('$DAEMON_VERSION', daemon_version), ('$DAEMON_SUBVERSION', network_info['subversion']), - ('$DONATION_ADDRESS', self.env.donation_address), + ('$PAYMENT_ADDRESS', self.env.payment_address), ]: banner = banner.replace(*pair) return banner - async def donation_address(self): - """Return the donation address as a string, empty if there is none.""" - return self.env.donation_address + async def payment_address(self): + """Return the payment address as a string, empty if there is none.""" + return self.env.payment_address async def banner(self): """Return the server banner text.""" diff --git a/lbry/wallet/usage_payment.py b/lbry/wallet/usage_payment.py new file mode 100644 index 000000000..563a0c5ea --- /dev/null +++ b/lbry/wallet/usage_payment.py @@ -0,0 +1,50 @@ +import asyncio +import logging + +from lbry.wallet import Wallet +from lbry.wallet.ledger import Ledger +from lbry.wallet.dewies import lbc_to_dewies +from lbry.wallet.transaction import Output, Transaction + +log = logging.getLogger(__name__) + + +class WalletServerPayer: + + def __init__(self, ledger: Ledger, wallet: Wallet): + self.ledger = ledger + self.wallet = wallet + self.running = False + self.task = None + + async def pay(self): + while self.running: + await asyncio.sleep(24 * 60 * 60) + features = await self.ledger.network.get_server_features() + address = features['payment_address'] + + if not self.ledger.is_valid_address(address): + raise Exception(f"Invalid address: {address}") + if self.wallet.is_locked: + raise Exception("Cannot spend funds with locked wallet") + + amount = lbc_to_dewies(features['daily_fee']) # check that this is in lbc and not dewies + # todo: check that amount is less than our max + + tx = await Transaction.create([], + [Output.pay_pubkey_hash(amount, self.ledger.address_to_hash160(address))], + self.wallet.get_accounts_or_all(None), + self.wallet.get_account_or_default(None)) + + await self.ledger.broadcast(tx) + await self.analytics_manager.send_credits_sent() + + async def start(self): + self.running = True + self.task = asyncio.ensure_future(self.pay()) + self.task.add_done_callback(lambda _: log.info("Stopping wallet server payments.")) + + async def stop(self): + if self.running: + self.running = False + self.task.cancel() diff --git a/tests/integration/blockchain/test_network.py b/tests/integration/blockchain/test_network.py index 2dea647ef..a4148a996 100644 --- a/tests/integration/blockchain/test_network.py +++ b/tests/integration/blockchain/test_network.py @@ -35,7 +35,7 @@ class NetworkTests(IntegrationTestCase): address = (await self.account.get_addresses(limit=1))[0] os.environ.update({ 'DESCRIPTION': 'Fastest server in the west.', - 'DONATION_ADDRESS': address, + 'PAYMENT_ADDRESS': address, 'DAILY_FEE': '42'}) await self.conductor.spv_node.start(self.conductor.blockchain_node) await self.ledger.network.on_connected.first diff --git a/tests/integration/blockchain/test_wallet_server_sessions.py b/tests/integration/blockchain/test_wallet_server_sessions.py index 59b44a450..8ed002165 100644 --- a/tests/integration/blockchain/test_wallet_server_sessions.py +++ b/tests/integration/blockchain/test_wallet_server_sessions.py @@ -3,7 +3,8 @@ import asyncio import lbry import lbry.wallet from lbry.wallet.network import ClientSession -from lbry.testcase import IntegrationTestCase +from lbry.testcase import IntegrationTestCase, CommandTestCase +from lbry.wallet.orchstr8.node import SPVNode class TestSessions(IntegrationTestCase): @@ -49,3 +50,40 @@ class TestSegwitServer(IntegrationTestCase): async def test_at_least_it_starts(self): await asyncio.wait_for(self.ledger.network.get_headers(0, 1), 1.0) + + +class TestUsagePayment(CommandTestCase): + + async def test_single_server_payment(self): + # create wallet server + # set payment address and fee rate on server + # connect to server + # fast forward 24 hours + # check that payment was sent to server + + address = (await self.account.receiving.get_addresses(limit=1, only_usable=True))[0] + + node = SPVNode(self.conductor.spv_module, node_number=2) + await node.start(self.blockchain, extraconf={"PAYMENT_ADDRESS": address, "DAILY_FEE": "1"}) + + self.ledger.network.config['default_servers'] = [(node.hostname, node.port)] + await self.ledger.stop() + await self.ledger.start() + + features = await self.ledger.network.get_server_features() + + + + pass + + # async def test_daily_payment(self): + # node2 = SPVNode(self.conductor.spv_module, node_number=2) + # self.ledger.network.config['default_servers'].append((node2.hostname, node2.port)) + # await asyncio.wait_for(self.ledger.stop(), timeout=1) + # await asyncio.wait_for(self.ledger.start(), timeout=1) + # self.ledger.network.session_pool.new_connection_event.clear() + # await node2.start(self.blockchain) + # # this is only to speed up the test as retrying would take 4+ seconds + # for session in self.ledger.network.session_pool.sessions: + # session.trigger_urgent_reconnect.set() + # await asyncio.wait_for(self.ledger.network.session_pool.new_connection_event.wait(), timeout=1) \ No newline at end of file