diff --git a/jmclient/jmclient/blockchaininterface.py b/jmclient/jmclient/blockchaininterface.py index 2402ff7..65a4cfc 100644 --- a/jmclient/jmclient/blockchaininterface.py +++ b/jmclient/jmclient/blockchaininterface.py @@ -292,18 +292,36 @@ class BitcoinCoreInterface(BlockchainInterface): self.import_addresses(addresses - imported_addresses, wallet_name) return import_needed - def _yield_transactions(self, wallet_name): - batch_size = 1000 - iteration = 0 + def _yield_transactions(self): + """ Generates a lazily fetched sequence of transactions seen in the + wallet (under any label/account), yielded in newest-first order. Care + is taken to avoid yielding duplicates even when new transactions are + actively being added to the wallet while the iteration is ongoing. + """ + num, skip = 1, 0 + txs = self.list_transactions(num, skip) + if not txs: + return + yielded_tx = txs[0] + yield yielded_tx while True: - new = self._rpc( - 'listtransactions', - ["*", batch_size, iteration * batch_size, True]) - for tx in new: - yield tx - if len(new) < batch_size: + num *= 2 + txs = self.list_transactions(num, skip) + if not txs: + return + try: + idx = [(tx['txid'], tx['vout'], tx['category']) for tx in txs + ].index((yielded_tx['txid'], yielded_tx['vout'], + yielded_tx['category'])) + except ValueError: + skip += num + continue + for tx in reversed(txs[:idx]): + yielded_tx = tx # inefficient but more obvious + yield yielded_tx + if len(txs) < num: return - iteration += 1 + skip += num - 1 def get_deser_from_gettransaction(self, rpcretval): """Get full transaction deserialization from a call @@ -641,7 +659,7 @@ class BitcoinCoreNoHistoryInterface(BitcoinCoreInterface, RegtestBitcoinCoreMixi assert desc_str.startswith("addr(") return desc_str[5:desc_str.find(")")] - def _yield_transactions(self, wallet_name): + def _yield_transactions(self): for u in self.scan_result["unspents"]: tx = {"category": "receive", "address": self._get_addr_from_desc(u["desc"])} diff --git a/jmclient/jmclient/wallet_service.py b/jmclient/jmclient/wallet_service.py index 332523e..d0b7952 100644 --- a/jmclient/jmclient/wallet_service.py +++ b/jmclient/jmclient/wallet_service.py @@ -669,8 +669,7 @@ class WalletService(Service): """ res = [] processed_txids = [] - for r in self.bci._yield_transactions( - self.get_wallet_name()): + for r in self.bci._yield_transactions(): txid = r["txid"] if txid not in processed_txids: tx = self.bci.get_transaction(hextobin(txid)) @@ -720,7 +719,7 @@ class WalletService(Service): if isinstance(self.wallet, FidelityBondMixin): tx_receive = [] burner_txes = [] - for tx in self.bci._yield_transactions(wallet_name): + for tx in self.bci._yield_transactions(): if tx['category'] == 'receive': tx_receive.append(tx) elif tx["category"] == "send": @@ -743,7 +742,7 @@ class WalletService(Service): else: #not fidelity bond wallet, significantly faster sync used_addresses_gen = set(tx['address'] - for tx in self.bci._yield_transactions(wallet_name) + for tx in self.bci._yield_transactions() if tx['category'] == 'receive') # needed for address-reuse check: self.used_addresses = used_addresses_gen