2019-02-11 23:46:13 +01:00
|
|
|
import json
|
2019-02-12 05:54:24 +01:00
|
|
|
import shutil
|
2019-02-11 23:46:13 +01:00
|
|
|
import tempfile
|
|
|
|
import logging
|
|
|
|
from binascii import unhexlify
|
|
|
|
|
2019-05-29 23:40:22 +02:00
|
|
|
from torba.testcase import IntegrationTestCase, WalletNode
|
2019-02-11 23:46:13 +01:00
|
|
|
|
2019-03-20 06:46:23 +01:00
|
|
|
import lbrynet.wallet
|
2019-02-11 23:46:13 +01:00
|
|
|
|
|
|
|
from lbrynet.conf import Config
|
|
|
|
from lbrynet.extras.daemon.Daemon import Daemon, jsonrpc_dumps_pretty
|
2019-03-20 06:46:23 +01:00
|
|
|
from lbrynet.wallet import LbryWalletManager
|
2019-02-12 05:54:24 +01:00
|
|
|
from lbrynet.extras.daemon.Components import Component, WalletComponent
|
2019-02-11 23:46:13 +01:00
|
|
|
from lbrynet.extras.daemon.Components import (
|
|
|
|
DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT,
|
|
|
|
UPNP_COMPONENT, EXCHANGE_RATE_MANAGER_COMPONENT
|
|
|
|
)
|
|
|
|
from lbrynet.extras.daemon.ComponentManager import ComponentManager
|
2019-02-12 05:54:24 +01:00
|
|
|
from lbrynet.extras.daemon.storage import SQLiteStorage
|
2019-03-28 19:51:55 +01:00
|
|
|
from lbrynet.blob.blob_manager import BlobManager
|
2019-02-12 05:54:24 +01:00
|
|
|
from lbrynet.stream.reflector.server import ReflectorServer
|
2019-02-12 17:30:40 +01:00
|
|
|
from lbrynet.blob_exchange.server import BlobServer
|
2019-02-12 05:54:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
class ExchangeRateManager:
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def convert_currency(self, from_currency, to_currency, amount):
|
|
|
|
return amount
|
|
|
|
|
|
|
|
def fee_dict(self):
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
class ExchangeRateManagerComponent(Component):
|
|
|
|
component_name = EXCHANGE_RATE_MANAGER_COMPONENT
|
|
|
|
|
|
|
|
def __init__(self, component_manager):
|
|
|
|
super().__init__(component_manager)
|
|
|
|
self.exchange_rate_manager = ExchangeRateManager()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def component(self) -> ExchangeRateManager:
|
|
|
|
return self.exchange_rate_manager
|
|
|
|
|
|
|
|
async def start(self):
|
|
|
|
self.exchange_rate_manager.start()
|
|
|
|
|
|
|
|
async def stop(self):
|
|
|
|
self.exchange_rate_manager.stop()
|
2019-02-11 23:46:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class CommandTestCase(IntegrationTestCase):
|
|
|
|
|
|
|
|
timeout = 180
|
2019-03-20 06:46:23 +01:00
|
|
|
LEDGER = lbrynet.wallet
|
2019-02-11 23:46:13 +01:00
|
|
|
MANAGER = LbryWalletManager
|
2019-03-22 08:14:33 +01:00
|
|
|
VERBOSITY = logging.WARN
|
2019-05-24 04:40:02 +02:00
|
|
|
blob_lru_cache_size = 0
|
2019-02-11 23:46:13 +01:00
|
|
|
|
|
|
|
async def asyncSetUp(self):
|
|
|
|
await super().asyncSetUp()
|
|
|
|
|
|
|
|
logging.getLogger('lbrynet.blob_exchange').setLevel(self.VERBOSITY)
|
|
|
|
logging.getLogger('lbrynet.daemon').setLevel(self.VERBOSITY)
|
2019-02-12 05:54:24 +01:00
|
|
|
logging.getLogger('lbrynet.stream').setLevel(self.VERBOSITY)
|
2019-02-11 23:46:13 +01:00
|
|
|
|
2019-05-29 23:40:22 +02:00
|
|
|
self.daemons = []
|
|
|
|
self.extra_wallet_nodes = []
|
|
|
|
self.extra_wallet_node_port = 5280
|
|
|
|
self.daemon = await self.add_daemon(self.wallet_node)
|
2019-02-11 23:46:13 +01:00
|
|
|
|
|
|
|
await self.account.ensure_address_gap()
|
|
|
|
address = (await self.account.receiving.get_addresses(limit=1, only_usable=True))[0]
|
|
|
|
sendtxid = await self.blockchain.send_to_address(address, 10)
|
|
|
|
await self.confirm_tx(sendtxid)
|
|
|
|
await self.generate(5)
|
|
|
|
|
2019-02-12 05:54:24 +01:00
|
|
|
server_tmp_dir = tempfile.mkdtemp()
|
|
|
|
self.addCleanup(shutil.rmtree, server_tmp_dir)
|
2019-04-15 22:14:19 +02:00
|
|
|
self.server_config = Config()
|
|
|
|
self.server_storage = SQLiteStorage(self.server_config, ':memory:')
|
2019-02-12 05:54:24 +01:00
|
|
|
await self.server_storage.open()
|
2019-04-15 22:14:19 +02:00
|
|
|
|
|
|
|
self.server_blob_manager = BlobManager(self.loop, server_tmp_dir, self.server_storage, self.server_config)
|
2019-02-12 17:30:40 +01:00
|
|
|
self.server = BlobServer(self.loop, self.server_blob_manager, 'bQEaw42GXsgCAGio1nxFncJSyRmnztSCjP')
|
|
|
|
self.server.start_server(5567, '127.0.0.1')
|
|
|
|
await self.server.started_listening.wait()
|
2019-02-12 05:54:24 +01:00
|
|
|
|
|
|
|
self.reflector = ReflectorServer(self.server_blob_manager)
|
|
|
|
self.reflector.start_server(5566, '127.0.0.1')
|
|
|
|
await self.reflector.started_listening.wait()
|
|
|
|
self.addCleanup(self.reflector.stop_server)
|
|
|
|
|
2019-02-11 23:46:13 +01:00
|
|
|
async def asyncTearDown(self):
|
|
|
|
await super().asyncTearDown()
|
2019-05-29 23:40:22 +02:00
|
|
|
for wallet_node in self.extra_wallet_nodes:
|
|
|
|
await wallet_node.stop(cleanup=True)
|
|
|
|
for daemon in self.daemons:
|
|
|
|
daemon.component_manager.get_component('wallet')._running = False
|
|
|
|
await daemon.stop(shutdown_runner=False)
|
|
|
|
|
|
|
|
async def add_daemon(self, wallet_node=None, seed=None):
|
|
|
|
if wallet_node is None:
|
|
|
|
wallet_node = WalletNode(
|
|
|
|
self.wallet_node.manager_class,
|
|
|
|
self.wallet_node.ledger_class,
|
|
|
|
port=self.extra_wallet_node_port
|
|
|
|
)
|
|
|
|
self.extra_wallet_node_port += 1
|
|
|
|
await wallet_node.start(self.conductor.spv_node, seed=seed)
|
|
|
|
self.extra_wallet_nodes.append(wallet_node)
|
|
|
|
|
|
|
|
conf = Config()
|
|
|
|
conf.data_dir = wallet_node.data_path
|
|
|
|
conf.wallet_dir = wallet_node.data_path
|
|
|
|
conf.download_dir = wallet_node.data_path
|
|
|
|
conf.share_usage_data = False
|
|
|
|
conf.use_upnp = False
|
|
|
|
conf.reflect_streams = True
|
|
|
|
conf.blockchain_name = 'lbrycrd_regtest'
|
|
|
|
conf.lbryum_servers = [('127.0.0.1', 50001)]
|
|
|
|
conf.reflector_servers = [('127.0.0.1', 5566)]
|
|
|
|
conf.known_dht_nodes = []
|
|
|
|
conf.blob_lru_cache_size = self.blob_lru_cache_size
|
|
|
|
conf.components_to_skip = [
|
|
|
|
DHT_COMPONENT, UPNP_COMPONENT, HASH_ANNOUNCER_COMPONENT,
|
|
|
|
PEER_PROTOCOL_SERVER_COMPONENT
|
|
|
|
]
|
|
|
|
|
|
|
|
def wallet_maker(component_manager):
|
|
|
|
wallet_component = WalletComponent(component_manager)
|
|
|
|
wallet_component.wallet_manager = wallet_node.manager
|
|
|
|
wallet_component._running = True
|
|
|
|
return wallet_component
|
|
|
|
|
|
|
|
daemon = Daemon(conf, ComponentManager(
|
|
|
|
conf, skip_components=conf.components_to_skip, wallet=wallet_maker,
|
|
|
|
exchange_rate_manager=ExchangeRateManagerComponent
|
|
|
|
))
|
|
|
|
await daemon.initialize()
|
2019-05-30 00:50:47 +02:00
|
|
|
self.daemons.append(daemon)
|
2019-05-29 23:40:22 +02:00
|
|
|
wallet_node.manager.old_db = daemon.storage
|
|
|
|
return daemon
|
2019-02-11 23:46:13 +01:00
|
|
|
|
|
|
|
async def confirm_tx(self, txid):
|
|
|
|
""" Wait for tx to be in mempool, then generate a block, wait for tx to be in a block. """
|
|
|
|
await self.on_transaction_id(txid)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_id(txid)
|
|
|
|
|
|
|
|
async def on_transaction_dict(self, tx):
|
|
|
|
await self.ledger.wait(
|
|
|
|
self.ledger.transaction_class(unhexlify(tx['hex']))
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_all_addresses(tx):
|
|
|
|
addresses = set()
|
|
|
|
for txi in tx['inputs']:
|
|
|
|
addresses.add(txi['address'])
|
|
|
|
for txo in tx['outputs']:
|
|
|
|
addresses.add(txo['address'])
|
|
|
|
return list(addresses)
|
|
|
|
|
|
|
|
async def generate(self, blocks):
|
|
|
|
""" Ask lbrycrd to generate some blocks and wait until ledger has them. """
|
|
|
|
await self.blockchain.generate(blocks)
|
|
|
|
await self.ledger.on_header.where(self.blockchain.is_expected_block)
|
|
|
|
|
2019-04-29 04:25:18 +02:00
|
|
|
async def blockchain_claim_name(self, name: str, value: str, amount: str, confirm=True):
|
2019-04-29 04:18:42 +02:00
|
|
|
txid = await self.blockchain._cli_cmnd('claimname', name, value, amount)
|
2019-04-29 04:25:18 +02:00
|
|
|
if confirm:
|
|
|
|
await self.generate(1)
|
2019-04-29 04:18:42 +02:00
|
|
|
return txid
|
2019-04-01 00:27:38 +02:00
|
|
|
|
2019-04-29 04:25:18 +02:00
|
|
|
async def blockchain_update_name(self, txid: str, value: str, amount: str, confirm=True):
|
2019-04-29 04:18:42 +02:00
|
|
|
txid = await self.blockchain._cli_cmnd('updateclaim', txid, value, amount)
|
2019-04-29 04:25:18 +02:00
|
|
|
if confirm:
|
|
|
|
await self.generate(1)
|
2019-04-29 04:18:42 +02:00
|
|
|
return txid
|
2019-04-29 00:19:58 +02:00
|
|
|
|
2019-02-11 23:46:13 +01:00
|
|
|
async def out(self, awaitable):
|
2019-04-06 21:55:08 +02:00
|
|
|
""" Serializes lbrynet API results to JSON then loads and returns it as dictionary. """
|
2019-02-11 23:46:13 +01:00
|
|
|
return json.loads(jsonrpc_dumps_pretty(await awaitable, ledger=self.ledger))['result']
|
|
|
|
|
2019-04-06 21:55:08 +02:00
|
|
|
def sout(self, value):
|
|
|
|
""" Synchronous version of `out` method. """
|
|
|
|
return json.loads(jsonrpc_dumps_pretty(value, ledger=self.ledger))['result']
|
|
|
|
|
2019-03-26 03:06:36 +01:00
|
|
|
async def stream_create(self, name='hovercraft', bid='1.0', data=b'hi!', confirm=True, **kwargs):
|
2019-05-08 20:47:04 +02:00
|
|
|
file = tempfile.NamedTemporaryFile()
|
|
|
|
|
|
|
|
def cleanup():
|
|
|
|
try:
|
|
|
|
file.close()
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.addCleanup(cleanup)
|
|
|
|
file.write(data)
|
|
|
|
file.flush()
|
|
|
|
claim = await self.out(
|
|
|
|
self.daemon.jsonrpc_stream_create(name, bid, file_path=file.name, **kwargs)
|
|
|
|
)
|
|
|
|
self.assertEqual(claim['outputs'][0]['name'], name)
|
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(claim)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(claim)
|
|
|
|
return claim
|
|
|
|
|
|
|
|
async def stream_update(self, claim_id, data=None, confirm=True, **kwargs):
|
|
|
|
if data:
|
|
|
|
file = tempfile.NamedTemporaryFile()
|
2019-02-11 23:46:13 +01:00
|
|
|
file.write(data)
|
|
|
|
file.flush()
|
2019-05-08 20:47:04 +02:00
|
|
|
|
|
|
|
def cleanup():
|
|
|
|
try:
|
|
|
|
file.close()
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.addCleanup(cleanup)
|
2019-03-24 21:55:04 +01:00
|
|
|
claim = await self.out(
|
2019-05-08 20:47:04 +02:00
|
|
|
self.daemon.jsonrpc_stream_update(claim_id, file_path=file.name, **kwargs)
|
2019-03-24 21:55:04 +01:00
|
|
|
)
|
|
|
|
else:
|
2019-03-26 03:06:36 +01:00
|
|
|
claim = await self.out(self.daemon.jsonrpc_stream_update(claim_id, **kwargs))
|
2019-03-24 21:55:04 +01:00
|
|
|
self.assertIsNotNone(claim['outputs'][0]['name'])
|
2019-02-11 23:46:13 +01:00
|
|
|
if confirm:
|
2019-03-24 21:55:04 +01:00
|
|
|
await self.on_transaction_dict(claim)
|
2019-02-11 23:46:13 +01:00
|
|
|
await self.generate(1)
|
2019-03-24 21:55:04 +01:00
|
|
|
await self.on_transaction_dict(claim)
|
|
|
|
return claim
|
|
|
|
|
2019-03-30 02:41:24 +01:00
|
|
|
async def stream_abandon(self, *args, confirm=True, **kwargs):
|
|
|
|
if 'blocking' not in kwargs:
|
|
|
|
kwargs['blocking'] = False
|
|
|
|
tx = await self.out(self.daemon.jsonrpc_stream_abandon(*args, **kwargs))
|
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(tx)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(tx)
|
|
|
|
return tx
|
|
|
|
|
2019-03-27 21:02:17 +01:00
|
|
|
async def publish(self, name, *args, confirm=True, **kwargs):
|
|
|
|
claim = await self.out(self.daemon.jsonrpc_publish(name, *args, **kwargs))
|
|
|
|
self.assertEqual(claim['outputs'][0]['name'], name)
|
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(claim)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(claim)
|
|
|
|
return claim
|
|
|
|
|
2019-03-26 03:06:36 +01:00
|
|
|
async def channel_create(self, name='@arena', bid='1.0', confirm=True, **kwargs):
|
2019-03-24 21:55:04 +01:00
|
|
|
channel = await self.out(self.daemon.jsonrpc_channel_create(name, bid, **kwargs))
|
|
|
|
self.assertEqual(channel['outputs'][0]['name'], name)
|
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(channel)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(channel)
|
|
|
|
return channel
|
|
|
|
|
2019-03-26 03:06:36 +01:00
|
|
|
async def channel_update(self, claim_id, confirm=True, **kwargs):
|
2019-03-24 21:55:04 +01:00
|
|
|
channel = await self.out(self.daemon.jsonrpc_channel_update(claim_id, **kwargs))
|
|
|
|
self.assertTrue(channel['outputs'][0]['name'].startswith('@'))
|
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(channel)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(channel)
|
2019-02-11 23:46:13 +01:00
|
|
|
return channel
|
|
|
|
|
2019-03-30 02:41:24 +01:00
|
|
|
async def channel_abandon(self, *args, confirm=True, **kwargs):
|
2019-03-26 03:06:36 +01:00
|
|
|
if 'blocking' not in kwargs:
|
|
|
|
kwargs['blocking'] = False
|
2019-03-30 02:41:24 +01:00
|
|
|
tx = await self.out(self.daemon.jsonrpc_channel_abandon(*args, **kwargs))
|
2019-03-26 03:06:36 +01:00
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(tx)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(tx)
|
|
|
|
return tx
|
|
|
|
|
2019-03-31 00:40:01 +01:00
|
|
|
async def support_create(self, claim_id, bid='1.0', confirm=True, **kwargs):
|
|
|
|
tx = await self.out(self.daemon.jsonrpc_support_create(claim_id, bid, **kwargs))
|
|
|
|
if confirm:
|
|
|
|
await self.on_transaction_dict(tx)
|
|
|
|
await self.generate(1)
|
|
|
|
await self.on_transaction_dict(tx)
|
|
|
|
return tx
|
|
|
|
|
2019-02-11 23:46:13 +01:00
|
|
|
async def resolve(self, uri):
|
|
|
|
return await self.out(self.daemon.jsonrpc_resolve(uri))
|
2019-03-26 03:06:36 +01:00
|
|
|
|
2019-04-29 06:38:58 +02:00
|
|
|
async def claim_search(self, **kwargs):
|
|
|
|
return (await self.out(self.daemon.jsonrpc_claim_search(**kwargs)))['items']
|