-
Notifications
You must be signed in to change notification settings - Fork 56
Jon/fix/xmr confs #696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Jon/fix/xmr confs #696
Conversation
2136688 to
3aa0896
Compare
| confirmations !== 'confirmed' && | ||
| confirmations !== 'dropped' && | ||
| confirmations !== 'failed' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems wrong. Suppose we did a cleaner like this:
const asEngineConfirmations = asEither(asNumber, asValue('confirmed', 'dropped', 'failed'))Those are the valid values for the number of confirmations. If the engine gives us one of those, we should just pass the value along unchanged. If the engine gives us something that is not one of those, the core should calculate the number of confirmations itself, using the block height and the tx height.
By removing "number" from the list, we are putting the core back in charge when the engine has already done this work. This creates its own bugs, because there are edge cases the core cannot handle. Long term, we want all the engines to calculate confirmations all the time, and the whole determineConfirmations path goes away.
However, what Paul's Cursor agent noticed is that we are mutating the transactions in Redux. This is the actual bug. The determineConfirmations function should not be updating anything in memory. Instead, Redux should just keep exactly what the engine gave us. The actual determineConfirmations should occur in combineTxWithFile, which is where we prepare transactions for the GUI's consumption, but only if we don't already have valid engine confirmations.
So, the onBlockHeightChanged handler is where we need to make the edits - stop calling reduxTx.confirmations =, which is an illegal state mutation, and instead just allow the existing combineTxWithFile to calculate the confirmations on the fly. Of course, combineTxWithFile might need to be updated to include the determineConfirmations call for this to work.
1fe20c9 to
07db443
Compare
6e01a45 to
73f8416
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
73f8416 to
8703aa7
Compare
CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
noneDescription
Problem
Monero transaction confirmations get stuck (e.g., at 0 or 1) and never progress to
'confirmed', even as blocks are mined.Root Cause
Two places were illegally mutating state before storing to Redux:
onBlockHeightChanged:onTransactions:The second issue was the actual blocker: once a numeric value like
1was stored in Redux,shouldCoreDetermineConfirmations(1)would returnfalse, soonBlockHeightChangedwould skip that transaction—confirmations never progressed.The Fix
Move confirmation calculation from storage time to read time:
onTransactionscalculated confirmations before storingonTransactionsstores raw engine values (includingundefined)onBlockHeightChangedmutatedreduxTx.confirmationsonBlockHeightChangedonly triggers GUI updates, no mutationscombineTxWithFile()undefinedstays in Redux, enabling continuous recalculationChanges
onTransactions()no longer pre-calculates confirmations:shouldCoreDetermineConfirmations/determineConfirmationsblockundefinedfor Monero)1,2,3were stored, causingonBlockHeightChangedto skip themcombineTxWithFile()now calculates confirmations on-the-fly:blockHeightif passed, otherwise falls back toinput.props.walletState.heightonBlockHeightChanged()no longer mutates state:heightdirectly tocombineTxWithFile()(avoids stale redux state from async dispatch)throttledOnTxChanged()CURRENCY_ENGINE_CHANGED_HEIGHTat the end for other consumersreduxTx.confirmations = ...CHANGE_MERGE_TXshouldCoreDetermineConfirmations()unchanged - still respects engine-provided values:number,'confirmed','dropped', or'failed'→ use itFlow: Engines That Manage Confirmations (Accountbased)
Flow: Engines That Don't Manage Confirmations (Monero, Bitcoin)
Key Points
Redux stores source of truth - Whatever the engine gave us (or
undefinedif nothing). BothonTransactionsandonBlockHeightChangednow preserve engine values without modification.Calculations happen at read time - In
combineTxWithFile(), not at storage time. This ensures every read gets fresh calculations based on current block height.No mutations - We never write
reduxTx.confirmations = ...ortx.confirmations = ...before storingEngine values are trusted - If engine provides a valid value (
number,'confirmed','dropped','failed'), we use it without recalculationEvery block triggers fresh calculation - For engines that don't manage confirmations,
onBlockHeightChangedcausescombineTxWithFile()to recalculate using the latest heightHeight passed directly -
onBlockHeightChangedpassesheightdirectly tocombineTxWithFile()because redux-pixies props don't update synchronously after dispatchNote
Fixes stuck confirmation states by moving confirmation calculation to read-time and avoiding Redux mutations.
combineTxWithFile()now accepts optionalblockHeightand calculatesconfirmationson-the-fly viadetermineConfirmationswhen engine values are missing/invalidonBlockHeightChanged()no longer mutates Redux; builds affectedEdgeTransactions with currentheight, batches viathrottledOnTxChanged, then dispatchesCURRENCY_ENGINE_CHANGED_HEIGHTshouldCoreDetermineConfirmationsand remove deprecated in-place confirmation recalculation path inonTransactionsWritten by Cursor Bugbot for commit 8703aa7. This will update automatically on new commits. Configure here.