failing test for unordered mempool

This commit is contained in:
Victor Shyba 2019-08-30 00:52:08 -03:00
parent 96800de052
commit 925eb618de
2 changed files with 45 additions and 2 deletions

View file

@ -1,6 +1,9 @@
import logging import logging
import asyncio import asyncio
import random
from itertools import chain from itertools import chain
from random import shuffle
from torba.testcase import IntegrationTestCase from torba.testcase import IntegrationTestCase
from torba.client.util import satoshis_to_coins, coins_to_satoshis from torba.client.util import satoshis_to_coins, coins_to_satoshis
@ -129,3 +132,39 @@ class BasicTransactionTests(IntegrationTestCase):
self.assertEqual(tx.outputs[0].get_address(self.ledger), address2) self.assertEqual(tx.outputs[0].get_address(self.ledger), address2)
self.assertEqual(tx.outputs[0].is_change, False) self.assertEqual(tx.outputs[0].is_change, False)
self.assertEqual(tx.outputs[1].is_change, True) self.assertEqual(tx.outputs[1].is_change, True)
async def test_history_edge_cases(self):
await self.assertBalance(self.account, '0.0')
address = await self.account.receiving.get_or_create_usable_address()
# evil trick: mempool is unsorted on real life, but same order between python instances. reproduce it
original_summary = self.conductor.spv_node.server.mempool.transaction_summaries
async def random_summary(*args, **kwargs):
summary = await original_summary(*args, **kwargs)
if summary and len(summary) > 2:
ordered = summary.copy()
while summary == ordered:
random.shuffle(summary)
return summary
self.conductor.spv_node.server.mempool.transaction_summaries = random_summary
# 10 unconfirmed txs, all from blockchain wallet
sends = list(self.blockchain.send_to_address(address, 10) for _ in range(10))
# use batching to reduce issues with send_to_address on cli
for batch in range(0, len(sends), 10):
txids = await asyncio.gather(*sends[batch:batch + 10])
await asyncio.wait([self.on_transaction_id(txid) for txid in txids])
remote_status = await self.ledger.network.subscribe_address(address)
self.assertTrue(await self.ledger.update_history(address, remote_status))
# 20 unconfirmed txs, 10 from blockchain, 10 from local to local
utxos = await self.account.get_utxos()
txs = []
for utxo in utxos:
tx = await self.ledger.transaction_class.create(
[self.ledger.transaction_class.input_class.spend(utxo)],
[],
[self.account], self.account
)
await self.broadcast(tx)
txs.append(tx)
await asyncio.wait([self.on_transaction_address(tx, address) for tx in txs], timeout=1)
remote_status = await self.ledger.network.subscribe_address(address)
self.assertTrue(await self.ledger.update_history(address, remote_status))

View file

@ -415,7 +415,7 @@ class BaseLedger(metaclass=LedgerRegistry):
local_status, local_history = await self.get_local_status_and_history(address) local_status, local_history = await self.get_local_status_and_history(address)
if local_status == remote_status: if local_status == remote_status:
return return True
remote_history = await self.network.retriable_call(self.network.get_history, address) remote_history = await self.network.retriable_call(self.network.get_history, address)
@ -472,14 +472,18 @@ class BaseLedger(metaclass=LedgerRegistry):
local_status, local_history = await self.get_local_status_and_history(address) local_status, local_history = await self.get_local_status_and_history(address)
if local_status != remote_status: if local_status != remote_status:
remote_history = list(map(itemgetter('tx_hash', 'height'), remote_history))
if remote_history == local_history:
return True
log.debug( log.debug(
"Wallet is out of sync after syncing. Remote: %s with %d items, local: %s with %d items", "Wallet is out of sync after syncing. Remote: %s with %d items, local: %s with %d items",
remote_status, len(remote_history), local_status, len(local_history) remote_status, len(remote_history), local_status, len(local_history)
) )
log.debug("local: %s", local_history) log.debug("local: %s", local_history)
log.debug("remote: %s", remote_history) log.debug("remote: %s", remote_history)
return False
else: else:
log.debug("Sync completed for: %s", address) return True
async def cache_transaction(self, txid, remote_height): async def cache_transaction(self, txid, remote_height):
cache_item = self._tx_cache.get(txid) cache_item = self._tx_cache.get(txid)