diff --git a/tests/integration/test_transactions.py b/tests/integration/test_transactions.py index 3f7cb73c3..4789c7a01 100644 --- a/tests/integration/test_transactions.py +++ b/tests/integration/test_transactions.py @@ -1,3 +1,4 @@ +import asyncio from orchstr8.testcase import IntegrationTestCase, d2f from torba.constants import COIN @@ -12,13 +13,18 @@ class BasicTransactionTests(IntegrationTestCase): self.assertEqual(await self.get_balance(account1), 0) self.assertEqual(await self.get_balance(account2), 0) - address1 = await d2f(account1.receiving.get_or_create_usable_address()) - sendtxid = await self.blockchain.send_to_address(address1.decode(), 5.5) - await self.on_transaction_id(sendtxid) # mempool + sendtxids = [] + for i in range(9): + address1 = await d2f(account1.receiving.get_or_create_usable_address()) + sendtxid = await self.blockchain.send_to_address(address1.decode(), 1.1) + sendtxids.append(sendtxid) + await self.on_transaction_id(sendtxid) # mempool await self.blockchain.generate(1) - await self.on_transaction_id(sendtxid) # confirmed + await asyncio.wait([ # confirmed + self.on_transaction_id(txid) for txid in sendtxids + ]) - self.assertEqual(round(await self.get_balance(account1)/COIN, 1), 5.5) + self.assertEqual(round(await self.get_balance(account1)/COIN, 1), 9.9) self.assertEqual(round(await self.get_balance(account2)/COIN, 1), 0) address2 = await d2f(account2.receiving.get_or_create_usable_address()) @@ -32,7 +38,5 @@ class BasicTransactionTests(IntegrationTestCase): await self.blockchain.generate(1) await self.on_transaction(tx) # confirmed - self.assertTrue(await d2f(self.ledger.is_valid_transaction(tx, 202))) - - self.assertEqual(round(await self.get_balance(account1)/COIN, 1), 3.5) + self.assertEqual(round(await self.get_balance(account1)/COIN, 1), 7.9) self.assertEqual(round(await self.get_balance(account2)/COIN, 1), 2.0) diff --git a/torba/baseheader.py b/torba/baseheader.py index db69b24ec..8815a6437 100644 --- a/torba/baseheader.py +++ b/torba/baseheader.py @@ -34,7 +34,13 @@ class BaseHeaders: @property def height(self): - return len(self) + return len(self)-1 + + def hash(self, height=None): + if height is None: + height = self.height + header = self[height] + return self._hash_header(header) def sync_read_length(self): return os.path.getsize(self.path) // self.header_size diff --git a/torba/baseledger.py b/torba/baseledger.py index d7ea7d671..5e0396bd3 100644 --- a/torba/baseledger.py +++ b/torba/baseledger.py @@ -203,7 +203,7 @@ class BaseLedger(six.with_metaclass(LedgerRegistry)): def update_headers(self): while True: height_sought = len(self.headers) - headers = yield self.network.get_headers(height_sought) + headers = yield self.network.get_headers(height_sought, 2000) if headers['count'] <= 0: break yield self.headers.connect(height_sought, unhexlify(headers['hex'])) @@ -266,34 +266,41 @@ class BaseLedger(six.with_metaclass(LedgerRegistry)): yield lock.acquire() - try: - # see if we have a local copy of transaction, otherwise fetch it from server - raw, local_height, is_verified = yield self.db.get_transaction(unhexlify(hex_id)[::-1]) - save_tx = None - if raw is None: - _raw = yield self.network.get_transaction(hex_id) - tx = self.transaction_class(unhexlify(_raw)) - save_tx = 'insert' - else: - tx = self.transaction_class(raw) + #try: + # see if we have a local copy of transaction, otherwise fetch it from server + raw, local_height, is_verified = yield self.db.get_transaction(unhexlify(hex_id)[::-1]) + save_tx = None + if raw is None: + _raw = yield self.network.get_transaction(hex_id) + tx = self.transaction_class(unhexlify(_raw)) + save_tx = 'insert' + else: + tx = self.transaction_class(raw) - if remote_height > 0 and not is_verified: - is_verified = yield self.is_valid_transaction(tx, remote_height) - is_verified = 1 if is_verified else 0 - if save_tx is None: - save_tx = 'update' + if remote_height > 0 and not is_verified: + is_verified = yield self.is_valid_transaction(tx, remote_height) + is_verified = 1 if is_verified else 0 + if save_tx is None: + save_tx = 'update' - yield self.db.save_transaction_io( - save_tx, tx, remote_height, is_verified, address, self.address_to_hash160(address), - ''.join('{}:{}:'.format(tx_id.decode(), tx_height) for tx_id, tx_height in synced_history) - ) + yield self.db.save_transaction_io( + save_tx, tx, remote_height, is_verified, address, self.address_to_hash160(address), + ''.join('{}:{}:'.format(tx_id.decode(), tx_height) for tx_id, tx_height in synced_history) + ) - self._on_transaction_controller.add(TransactionEvent(address, tx, remote_height, is_verified)) + log.debug("{}: sync'ed tx {} for address: {}, height: {}, verified: {}".format( + self.get_id(), hex_id, address, remote_height, is_verified + )) + self._on_transaction_controller.add(TransactionEvent(address, tx, remote_height, is_verified)) - finally: - lock.release() - if not lock.locked: - del self._transaction_processing_locks[hex_id] +# except: +# log.exception('Failed to synchronize transaction:') +# raise +# +# finally: + lock.release() + if not lock.locked: + del self._transaction_processing_locks[hex_id] @defer.inlineCallbacks def subscribe_history(self, address): diff --git a/torba/basenetwork.py b/torba/basenetwork.py index 509eea87f..44720a0c6 100644 --- a/torba/basenetwork.py +++ b/torba/basenetwork.py @@ -11,7 +11,10 @@ from twisted.protocols.basic import LineOnlyReceiver from torba import __version__ from torba.stream import StreamController -log = logging.getLogger() +log = logging.getLogger(__name__) + +if six.PY3: + buffer = memoryview def unicode2bytes(string): @@ -23,6 +26,8 @@ def unicode2bytes(string): def bytes2unicode(maybe_bytes): + if isinstance(maybe_bytes, buffer): + maybe_bytes = str(maybe_bytes) if isinstance(maybe_bytes, bytes): return maybe_bytes.decode() elif isinstance(maybe_bytes, (list, tuple)): @@ -32,7 +37,7 @@ def bytes2unicode(maybe_bytes): class StratumClientProtocol(LineOnlyReceiver): delimiter = b'\n' - MAX_LENGTH = 100000 + MAX_LENGTH = 2000000 def __init__(self): self.request_id = 0 @@ -78,6 +83,7 @@ class StratumClientProtocol(LineOnlyReceiver): self.on_disconnected_controller.add(True) def lineReceived(self, line): + log.debug('received: {}'.format(line)) try: # `line` comes in as a byte string but `json.loads` automatically converts everything to @@ -114,6 +120,7 @@ class StratumClientProtocol(LineOnlyReceiver): 'method': method, 'params': [bytes2unicode(arg) for arg in args] }) + log.debug('sent: {}'.format(message)) self.sendLine(message.encode('latin-1')) d = self.lookup_table[message_id] = defer.Deferred() return d @@ -161,17 +168,19 @@ class BaseNetwork: def start(self): for server in cycle(self.config['default_servers']): endpoint = clientFromString(reactor, 'tcp:{}:{}'.format(*server)) + log.debug("Attempting connection to SPV wallet server: {}:{}".format(*server)) self.service = ClientService(endpoint, StratumClientFactory(self)) self.service.startService() try: self.client = yield self.service.whenConnected(failAfterFailures=2) yield self.ensure_server_version() + log.info("Successfully connected to SPV wallet server: {}:{}".format(*server)) self._on_connected_controller.add(True) yield self.client.on_disconnected.first except CancelledError: return - except Exception as e: - pass + except Exception: + log.exception("Connecting to {}:{} raised an exception:".format(*server)) finally: self.client = None if not self.running: diff --git a/torba/manager.py b/torba/manager.py index 42f8e58b2..3dee2064e 100644 --- a/torba/manager.py +++ b/torba/manager.py @@ -15,14 +15,13 @@ class WalletManager(object): @classmethod def from_config(cls, config): # type: (Dict) -> WalletManager - wallets = [] - manager = cls(wallets) - for coin_id, ledger_config in config.get('ledgers', {}).items(): - manager.get_or_create_ledger(coin_id, ledger_config) + manager = cls() + for ledger_id, ledger_config in config.get('ledgers', {}).items(): + manager.get_or_create_ledger(ledger_id, ledger_config) for wallet_path in config.get('wallets', []): wallet_storage = WalletStorage(wallet_path) wallet = Wallet.from_storage(wallet_storage, manager) - wallets.append(wallet) + manager.wallets.append(wallet) return manager def get_or_create_ledger(self, ledger_id, ledger_config=None): diff --git a/torba/wallet.py b/torba/wallet.py index 9ac2d9f76..0ff419089 100644 --- a/torba/wallet.py +++ b/torba/wallet.py @@ -59,12 +59,11 @@ class Wallet: class WalletStorage: - LATEST_VERSION = 2 + LATEST_VERSION = 1 DEFAULT = { 'version': LATEST_VERSION, 'name': 'Wallet', - 'coins': {}, 'accounts': [] } @@ -100,10 +99,25 @@ class WalletStorage: version = json_dict.pop('version', -1) - if version == 1: # upgrade from version 1 to version 2 - _rename_property('addr_history', 'history') - _rename_property('use_encryption', 'encrypted') - _rename_property('gap_limit', 'gap_limit_for_receiving') + if version == -1: # upgrading from electrum wallet + json_dict = { + 'accounts': [{ + 'ledger': 'lbc_mainnet', + 'encrypted': json_dict['use_encryption'], + 'seed': json_dict['seed'], + 'seed_version': json_dict['seed_version'], + 'private_key': json_dict['master_private_keys']['x/'], + 'public_key': json_dict['master_public_keys']['x/'], + 'certificates': json_dict['claim_certificates'], + 'receiving_gap': 20, + 'change_gap': 6, + 'receiving_maximum_use_per_address': 2, + 'change_maximum_use_per_address': 2 + }] + } + + elif version == 1: # upgrade from version 1 to version 2 + pass upgraded = cls.DEFAULT upgraded.update(json_dict)