From 1b9eb8b1b9d9b64b106291dd789081fa73fb4a7b Mon Sep 17 00:00:00 2001 From: zebra-lucky Date: Fri, 10 Oct 2025 06:50:50 +0300 Subject: [PATCH] fix asyncio.iscoroutine usage --- src/jmclient/client_protocol.py | 23 +++++----- src/jmclient/taker.py | 76 ++++++++++++++++----------------- src/jmclient/wallet.py | 29 +++++-------- src/jmclient/wallet_service.py | 29 ++++++------- src/jmclient/wallet_utils.py | 10 ++--- src/jmclient/yieldgenerator.py | 8 ++-- 6 files changed, 81 insertions(+), 94 deletions(-) diff --git a/src/jmclient/client_protocol.py b/src/jmclient/client_protocol.py index f6adf80..c1ed006 100644 --- a/src/jmclient/client_protocol.py +++ b/src/jmclient/client_protocol.py @@ -143,10 +143,9 @@ class BIP78ClientProtocol(BaseClientProtocol): @commands.BIP78SenderReceiveProposal.responder async def on_BIP78_SENDER_RECEIVE_PROPOSAL(self, psbt): - if asyncio.iscoroutine(self.success_callback): - await self.success_callback(psbt, self.manager) - else: - self.success_callback(psbt, self.manager) + cb_res = self.success_callback(psbt, self.manager) + if asyncio.iscoroutine(cb_res): + cb_res = await cb_res return {"accepted": True} @commands.BIP78SenderReceiveError.responder @@ -954,8 +953,8 @@ class JMTakerClientProtocol(JMClientProtocol): jlog.info("Stall detected. Retrying transaction if possible ...") finished_cb_res = self.client.on_finished_callback( False, True, 0.0) - if asyncio.iscoroutine(self.client.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res else: #This shouldn't really happen; if the tx confirmed, #the finished callback should already be called. @@ -1008,8 +1007,8 @@ class JMTakerClientProtocol(JMClientProtocol): # the logic here is the same. finished_cb_res = self.client.on_finished_callback( False, False, 0.0) - if asyncio.iscoroutine(self.client.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res return {'accepted': False} else: nick_list, tx = retval[1:] @@ -1034,16 +1033,16 @@ class JMTakerClientProtocol(JMClientProtocol): #but is not the functionality desired in general (tumbler). finished_cb_res = self.client.on_finished_callback( False, False, 0.0) - if asyncio.iscoroutine(self.client.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res return {'accepted': True} elif retval[0] == "commitment-failure": #This case occurs if we cannot find any utxos for reasons #other than age, which is a permanent failure finished_cb_res = self.client.on_finished_callback( False, False, 0.0) - if asyncio.iscoroutine(self.client.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res return {'accepted': True} amt, cmt, rev, foffers = retval[1:] d = self.callRemote(commands.JMFill, diff --git a/src/jmclient/taker.py b/src/jmclient/taker.py index a4f446a..8344f3d 100644 --- a/src/jmclient/taker.py +++ b/src/jmclient/taker.py @@ -184,18 +184,18 @@ class Taker(object): return (False,) info_cb_res = self.taker_info_callback( "INFO", "Received offers from joinmarket pit") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res #choose the next item in the schedule self.schedule_index += 1 if self.schedule_index == len(self.schedule): info_cb_res = self.taker_info_callback( "INFO", "Finished all scheduled transactions") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res finished_cb_res = self.on_finished_callback(True) - if asyncio.iscoroutine(self.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res return (False,) else: #read the settings from the schedule entry @@ -256,8 +256,8 @@ class Taker(object): #choose coins to spend info_cb_res = self.taker_info_callback( "INFO", "Preparing bitcoin data..") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res if not await self.prepare_my_bitcoin_data(): return (False,) #Prepare a commitment @@ -270,18 +270,18 @@ class Taker(object): #(TODO, it's possible for user to dynamically add more coins, #consider if this option means we should stay alive). info_cb_res = self.taker_info_callback("ABORT", errmsg) - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return ("commitment-failure",) else: info_cb_res = self.taker_info_callback("INFO", errmsg) - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return (False,) else: info_cb_res = self.taker_info_callback("INFO", errmsg) - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res #Initialization has been successful. We must set the nonrespondants #now to keep track of what changed when we receive the utxo data @@ -325,7 +325,7 @@ class Taker(object): accepted = self.filter_orders_callback([self.orderbook, self.total_cj_fee], self.cjamount) - if asyncio.iscoroutine(self.filter_orders_callback): + if asyncio.iscoroutine(accepted): accepted = await accepted if accepted == "retry": #Special condition if Taker is "determined to continue" @@ -361,8 +361,8 @@ class Taker(object): except: info_cb_res = self.taker_info_callback( "ABORT", "Failed to get a change address") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return False #adjust the required amount upwards to anticipate an increase in #transaction fees after re-estimation; this is sufficiently conservative @@ -381,8 +381,8 @@ class Taker(object): except Exception as e: info_cb_res = self.taker_info_callback( "ABORT", "Unable to select sufficient coins: " + repr(e)) - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return False else: #sweep @@ -423,14 +423,14 @@ class Taker(object): if not self.orderbook: info_cb_res = self.taker_info_callback( "ABORT", "Could not find orders to complete transaction") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return False if self.filter_orders_callback: accepted = self.filter_orders_callback((self.orderbook, self.total_cj_fee), self.cjamount) - if asyncio.iscoroutine(self.filter_orders_callback): + if asyncio.iscoroutine(accepted): accepted = await accepted if not accepted: return False @@ -482,15 +482,15 @@ class Taker(object): "POLICY", "minimum_makers"): info_cb_res = self.taker_info_callback( "INFO", "Not enough counterparties, aborting.") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return (False, "Not enough counterparties responded to fill, giving up") info_cb_res = self.taker_info_callback( "INFO", "Got all parts, enough to build a tx") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res #The list self.nonrespondants is now reset and #used to track return of signatures for phase 2 @@ -583,8 +583,8 @@ class Taker(object): info_cb_res = self.taker_info_callback( "INFO", "Built tx, sending to counterparties.") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res return (True, list(self.maker_utxo_data.keys()), self.latest_tx.serialize()) @@ -846,8 +846,8 @@ class Taker(object): jlog.info('all makers have sent their signatures') info_cb_res = self.taker_info_callback( "INFO", "Transaction is valid, signing..") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res jlog.debug("schedule item was: " + str(self.schedule[self.schedule_index])) return await self.self_sign_and_push() @@ -1001,8 +1001,8 @@ class Taker(object): "node. The transaction is NOT broadcast.") info_cb_res = self.taker_info_callback( "ABORT", warnmsg + "\nSee log for details.") - if asyncio.iscoroutine(self.taker_info_callback): - await info_cb_res + if asyncio.iscoroutine(info_cb_res): + info_cb_res = await info_cb_res # warning is arguably not correct but it will stand out more: jlog.warn(warnmsg) jlog.info(btc.human_readable_transaction(tx)) @@ -1059,8 +1059,8 @@ class Taker(object): pushed = self.push_ourselves() if not pushed: finished_cb_res = self.on_finished_callback(False, fromtx=True) - if asyncio.iscoroutine(self.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res else: if nick_to_use: return (nick_to_use, self.latest_tx.serialize()) @@ -1084,8 +1084,8 @@ class Taker(object): jlog.info("Transaction seen on network, waiting for confirmation") #To allow client to mark transaction as "done" (e.g. by persisting state) finished_cb_res= self.on_finished_callback(True, fromtx="unconfirmed") - if asyncio.iscoroutine(self.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res self.waiting_for_conf = True confirm_timeout_sec = float(jm_single().config.get( "TIMEOUT", "confirm_timeout_hours")) * 3600 @@ -1109,8 +1109,8 @@ class Taker(object): waittime = self.schedule[self.schedule_index][4] finished_cb_res = self.on_finished_callback( True, fromtx=fromtx, waittime=waittime, txdetails=(txd, txid)) - if asyncio.iscoroutine(self.on_finished_callback): - await finished_cb_res + if asyncio.iscoroutine(finished_cb_res): + finished_cb_res = await finished_cb_res return True def _is_our_input(self, tx_input): diff --git a/src/jmclient/wallet.py b/src/jmclient/wallet.py index a94670f..3ca168b 100644 --- a/src/jmclient/wallet.py +++ b/src/jmclient/wallet.py @@ -2334,24 +2334,17 @@ class SNICKERWalletMixin(object): assert unsigned_index != -1 # All validation checks passed. We now check whether the #transaction is acceptable according to the caller: - if asyncio.iscoroutine(acceptance_callback): - if not await acceptance_callback( - [utx.vin[unsigned_index]], - [x for i, x in enumerate(utx.vin) - if i != unsigned_index], - [utx.vout[our_output_index]], - [x for i, x in enumerate(utx.vout) - if i != our_output_index]): - return None, "Caller rejected transaction for signing." - else: - if not acceptance_callback( - [utx.vin[unsigned_index]], - [x for i, x in enumerate(utx.vin) - if i != unsigned_index], - [utx.vout[our_output_index]], - [x for i, x in enumerate(utx.vout) - if i != our_output_index]): - return None, "Caller rejected transaction for signing." + cb_res = acceptance_callback( + [utx.vin[unsigned_index]], + [x for i, x in enumerate(utx.vin) + if i != unsigned_index], + [utx.vout[our_output_index]], + [x for i, x in enumerate(utx.vout) + if i != our_output_index]) + if asyncio.iscoroutine(cb_res): + cb_res = await cb_res + if not cb_res: + return None, "Caller rejected transaction for signing." # Acceptance passed, prepare the deserialized tx for signing by us: signresult_and_signedpsbt, err = await self.sign_psbt( diff --git a/src/jmclient/wallet_service.py b/src/jmclient/wallet_service.py index 83427f8..aa00632 100644 --- a/src/jmclient/wallet_service.py +++ b/src/jmclient/wallet_service.py @@ -391,10 +391,9 @@ class WalletService(Service): for f in self.callbacks["all"]: # note we need no return value as we will never # remove these from the list - if asyncio.iscoroutine(f): - await f(txd, txid) - else: - f(txd, txid) + cb_res = f(txd, txid) + if asyncio.iscoroutine(cb_res): + cb_res = await cb_res # txid is not always available at the time of callback registration. # Migrate any callbacks registered under the provisional key, and @@ -422,12 +421,11 @@ class WalletService(Service): if confs == 0: callbacks = [] for f in self.callbacks["unconfirmed"].pop(txid, []): - if asyncio.iscoroutine(f): - if not await f(txd, txid): - callbacks.append(f) - else: - if not f(txd, txid): - callbacks.append(f) + cb_res = f(txd, txid) + if asyncio.iscoroutine(cb_res): + cb_res = await cb_res + if not cb_res: + callbacks.append(f) if callbacks: self.callbacks["unconfirmed"][txid] = callbacks else: @@ -440,12 +438,11 @@ class WalletService(Service): elif confs > 0: callbacks = [] for f in self.callbacks["confirmed"].pop(txid, []): - if asyncio.iscoroutine(f): - if not await f(txd, txid, confs): - callbacks.append(f) - else: - if not f(txd, txid, confs): - callbacks.append(f) + cb_res = f(txd, txid, confs) + if asyncio.iscoroutine(cb_res): + cb_res = await cb_res + if not cb_res: + callbacks.append(f) if callbacks: self.callbacks["confirmed"][txid] = callbacks else: diff --git a/src/jmclient/wallet_utils.py b/src/jmclient/wallet_utils.py index 5b9d68b..c62102c 100644 --- a/src/jmclient/wallet_utils.py +++ b/src/jmclient/wallet_utils.py @@ -1390,12 +1390,10 @@ async def wallet_freezeutxo(wallet_service, md, info_callback("The mixdepth: " + str(md) + \ " contains no utxos to freeze/unfreeze.", "error") return "Failed" - if asyncio.iscoroutine(display_callback): - display_ret = await display_callback(wallet_service, - utxos_enabled, utxos_disabled) - else: - display_ret = display_callback(wallet_service, - utxos_enabled, utxos_disabled) + display_ret = display_callback( + wallet_service, utxos_enabled, utxos_disabled) + if asyncio.iscoroutine(display_ret): + display_ret = await display_ret if display_ret is None: break if display_ret == "all": diff --git a/src/jmclient/yieldgenerator.py b/src/jmclient/yieldgenerator.py index bf3554b..742aaa9 100644 --- a/src/jmclient/yieldgenerator.py +++ b/src/jmclient/yieldgenerator.py @@ -312,10 +312,10 @@ class YieldGeneratorService(Service): # we do not catch Exceptions in setup, # deliberately; this must be caught and distinguished # by whoever started the service. - if asyncio.iscoroutine(setup): - raise NotImplementedError() # FIXME - else: - setup() + setup_res = setup() + if asyncio.iscoroutine(setup_res): + raise Exception('YieldGeneratorService can not have ' + 'asyncio setup functions') # TODO genericise to any YG class: self.yieldgen = YieldGeneratorBasic(self.wallet_service, self.yg_config)