wallet: announce missed confirm transition when tx unlocks between polls
What changed, and why it matters
This commit fixes a notification bug in a Monero wallet library. Previously, if a transaction both confirmed and became unlocked between two polling checks (which can happen during fast block times), the wallet would only announce the 'unlocked' state and skip the 'confirmed' state notification. The patch adds logic to detect this skipped transition and emit the missed 'confirmed' notification before announcing the 'unlocked' state. It is a functional/event-ordering fix, not a security vulnerability fix.
No security action required. Treat as a normal bug fix and include in regular release testing, particularly for wallets relying on accurate per-state output notifications during fast-confirmation scenarios.
Security signals we found
No security-relevant signals present in commit message or diff
Change is a state-transition notification ordering fix
No input validation, cryptographic, authorization, or memory-safety changes
Evidence from the diff
In MoneroWalletRpc.poll(), when iterating over newly unlocked transactions, the code now checks whether the transaction is confirmed but was not previously announced as confirmed (!prevConfirmedNotifications.contains(hash)). If so, it synthesizes a confirmed-but-still-locked copy of the transaction, wires it into its block, calls notifyOutputs(confirmedTx), and then proceeds with the existing notifyOutputs(unlockedTx). This ensures listeners receive both CONFIRMED and UNLOCKED output notifications even when the wallet polls too coarsely to observe the intermediate confirmed-locked state.
Changed components
src/main/java/monero/wallet/MoneroWalletRpc.javaMoneroWalletRpc.poll()Output notification event pipelineInspect captured patch +6 / −0
### src/main/java/monero/wallet/MoneroWalletRpc.java
@@ -2421,8 +2421,14 @@ public void poll() {
// announce new unlocked outputs
for (MoneroTxWallet unlockedTx : unlockedTxs) {
+ boolean missedConfirm = Boolean.TRUE.equals(unlockedTx.isConfirmed()) && !prevConfirmedNotifications.contains(unlockedTx.getHash());
prevUnconfirmedNotifications.remove(unlockedTx.getHash()); // stop tracking tx notifications
prevConfirmedNotifications.remove(unlockedTx.getHash());
+ if (missedConfirm) { // announce missed confirm transition if tx unlocked between polls
+ MoneroTxWallet confirmedTx = unlockedTx.copy().setIsLocked(true);
+ confirmedTx.setBlock(unlockedTx.getBlock().copy().setTxs(confirmedTx));
+ notifyOutputs(confirmedTx);
+ }
notifyOutputs(unlockedTx);
}
Why this scored 22/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.