core+gui: name the real cause of a failed build instead of guessing
The Building Will report showed a fixed list of three "possible reasons"
whenever a build produced nothing, regardless of what actually happened; in a
case reproduced from the owner log all three were false and the real cause was
not even listed. The "Checking your will" row had the same flaw, showing one
sentence ("Found CHANGES to the DATE or the HEIRS") for five situations,
including one where it is plainly wrong (funds received).
core/heirs.py: record WHY buildTransactions gave up in a new last_build_error
attribute (8 reason codes), set at each path that previously returned empty
with no explanation, plus a processed_willexecutors counter to tell "every
will-executor was skipped" apart from "we tried and failed". Also fix a latent
crash in the prepare_transactions handler, which read a no-longer-existing
e.heirname attribute and re-raised the resulting AttributeError, masking the
real error.
gui/qt/dialogs.py: add msg_alert() (amber warning sign, body text in the theme
colour, readable in both themes), _build_failure_message() and
_check_failure_message() to turn those causes into one precise sentence each,
with an honest "cause could not be determined" fallback. Catch
BalanceTooLowException, which already carried the figures but fell through to
the generic red technical error.
No new exception classes were introduced (owner request): the plain
NotCompleteWillException cases are told apart structurally, not by text.
This commit is contained in:
@@ -363,6 +363,10 @@ class Heirs(dict, Logger):
|
||||
Logger.__init__(self)
|
||||
self.db = wallet.db
|
||||
self.wallet = wallet
|
||||
# Reason code explaining why the last buildTransactions() produced no
|
||||
# transaction (None when the last build succeeded or never ran). See
|
||||
# buildTransactions for the list of codes and why they exist.
|
||||
self.last_build_error = None
|
||||
d = self.db.get("heirs", {})
|
||||
try:
|
||||
self.update(d)
|
||||
@@ -630,6 +634,20 @@ class Heirs(dict, Logger):
|
||||
def buildTransactions(
|
||||
self, bal_plugin, wallet, tx_fees=None, utxos=None, from_locktime=0
|
||||
):
|
||||
# Reset the diagnostic reason at the start of every build attempt.
|
||||
#
|
||||
# WHY: when the build produced nothing, the GUI used to show a fixed
|
||||
# list of three "possible reasons" (low balance / dust shares /
|
||||
# check-alive after the delivery date). In practice the real cause is
|
||||
# often NONE of those three - several code paths below simply return
|
||||
# an empty result with no explanation at all, so the user was shown
|
||||
# three guesses that were all wrong. Each such path now records WHY
|
||||
# it gave up, and BalBuildWillDialog names the actual cause.
|
||||
#
|
||||
# Codes: NO_HEIRS, NO_UTXO, NO_WILLEXECUTOR_USABLE, NO_FUTURE_DATE,
|
||||
# WILLEXECUTOR_FEE, WILLEXECUTOR_FEE_TOO_HIGH, TX_BUILD_FAILED,
|
||||
# WILLEXECUTOR_TX_ERROR.
|
||||
self.last_build_error = None
|
||||
_before = list(self.keys())
|
||||
Heirs._validate(self, persist=False)
|
||||
_removed = [k for k in _before if k not in self]
|
||||
@@ -644,6 +662,7 @@ class Heirs(dict, Logger):
|
||||
", ".join(_removed),
|
||||
)
|
||||
if len(self) <= 0:
|
||||
self.last_build_error = "NO_HEIRS"
|
||||
_logger.info("while building transactions there was no heirs")
|
||||
return
|
||||
balance = 0.0
|
||||
@@ -660,12 +679,18 @@ class Heirs(dict, Logger):
|
||||
len_utxo_set += 1
|
||||
available_utxos.append(utxo)
|
||||
if len_utxo_set == 0:
|
||||
self.last_build_error = "NO_UTXO"
|
||||
_logger.info("no usable utxos")
|
||||
return
|
||||
j = -2
|
||||
willexecutorsitems = list(willexecutors.items())
|
||||
willexecutorslen = len(willexecutorsitems)
|
||||
alltxs = {}
|
||||
# Counts how many will-executors were actually PROCESSED (i.e. passed
|
||||
# the is_selected/is_valid filter below and reached the build loop).
|
||||
# If it stays 0 the loop silently skipped every single one, which is a
|
||||
# distinct failure from "we tried and the build failed".
|
||||
processed_willexecutors = 0
|
||||
while True:
|
||||
j += 1
|
||||
if j >= willexecutorslen:
|
||||
@@ -682,6 +707,7 @@ class Heirs(dict, Logger):
|
||||
url = willexecutor = None
|
||||
else:
|
||||
break
|
||||
processed_willexecutors += 1
|
||||
fees = {}
|
||||
i = 0
|
||||
txs = {}
|
||||
@@ -699,9 +725,11 @@ class Heirs(dict, Logger):
|
||||
max_fee=bal_plugin.MAX_WILLEXECUTOR_FEE.get(),
|
||||
)
|
||||
except WillExecutorFeeException:
|
||||
self.last_build_error = "WILLEXECUTOR_FEE"
|
||||
i = 10
|
||||
continue
|
||||
except WillExecutorFeeTooHighException:
|
||||
self.last_build_error = "WILLEXECUTOR_FEE_TOO_HIGH"
|
||||
i = 10
|
||||
continue
|
||||
if locktimes:
|
||||
@@ -710,19 +738,33 @@ class Heirs(dict, Logger):
|
||||
locktimes, available_utxos[:], fees, wallet
|
||||
)
|
||||
if not txs:
|
||||
self.last_build_error = "TX_BUILD_FAILED"
|
||||
return {}
|
||||
except Exception as e:
|
||||
# An unexpected failure while assembling the
|
||||
# transactions for THIS will-executor.
|
||||
#
|
||||
# WHY THIS CHANGED: the previous code read
|
||||
# ``e.heirname`` here, in order to auto-deselect the
|
||||
# will-executor blamed by the exception. NOTHING in
|
||||
# the plugin sets that attribute any more (it is a
|
||||
# leftover from an older exception design), so the
|
||||
# lookup itself raised AttributeError, and the inner
|
||||
# ``except Exception: raise`` re-raised THAT - aborting
|
||||
# the whole build with a confusing secondary error
|
||||
# instead of the real one. We now record the reason,
|
||||
# log the actual exception together with the
|
||||
# will-executor it happened on, and simply move on to
|
||||
# the next one, which is what the original code was
|
||||
# clearly trying to do.
|
||||
self.last_build_error = "WILLEXECUTOR_TX_ERROR"
|
||||
_logger.error(
|
||||
f"build transactions: error preparing transactions: {e}"
|
||||
"build transactions: error preparing transactions "
|
||||
"for will-executor %s: %r",
|
||||
(willexecutor or {}).get("url", "(none)"),
|
||||
e,
|
||||
)
|
||||
try:
|
||||
if "w!ll3x3c" in e.heirname:
|
||||
Willexecutors.is_selected(
|
||||
e.heirname[len("w!ll3x3c") :], False
|
||||
)
|
||||
break
|
||||
except Exception:
|
||||
raise
|
||||
break
|
||||
total_fees = 0
|
||||
total_fees_real = 0
|
||||
total_in = 0
|
||||
@@ -746,12 +788,26 @@ class Heirs(dict, Logger):
|
||||
if i >= 10:
|
||||
break
|
||||
else:
|
||||
self.last_build_error = "NO_FUTURE_DATE"
|
||||
_logger.info(
|
||||
f"no locktimes for willexecutor {willexecutor} skipped"
|
||||
)
|
||||
break
|
||||
alltxs.update(txs)
|
||||
|
||||
# Every will-executor was skipped by the is_selected/is_valid filter
|
||||
# (or the list was empty) and no "no will-executor" build was allowed,
|
||||
# so the loop above never even attempted a build. This path used to
|
||||
# return silently with no log line at all, which is exactly the case
|
||||
# the owner hit: the dialog then blamed balance/dust/check-alive, none
|
||||
# of which was true.
|
||||
if not alltxs and processed_willexecutors == 0:
|
||||
self.last_build_error = "NO_WILLEXECUTOR_USABLE"
|
||||
_logger.info(
|
||||
"no usable will-executor: all %d skipped (not selected or not valid)",
|
||||
willexecutorslen,
|
||||
)
|
||||
|
||||
return alltxs
|
||||
|
||||
def get_transactions(
|
||||
|
||||
Reference in New Issue
Block a user