-
Notifications
You must be signed in to change notification settings - Fork 116
Fix Invalid Pending Payments and Strengthen RBF Tests #647
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: main
Are you sure you want to change the base?
Fix Invalid Pending Payments and Strengthen RBF Tests #647
Conversation
I've assigned @tnull as a reviewer! |
🔔 1st Reminder Hey @tnull! This PR has been waiting for your review. |
src/wallet/mod.rs
Outdated
} else { | ||
PaymentStatus::Pending | ||
}; | ||
if payment_status == PaymentStatus::Pending { |
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.
Thanks for the PR. Since PaymentStatus::Pending
is only assigned in the else branch above, we can move the unconfirmed_txid.push(id)
call directly into that block. This avoids the extra comparison afterward and makes the logic a bit more concise and self-contained.
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.
Done!
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.
Great! Thanks
56a50c1
to
569cebe
Compare
🔔 2nd Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 3rd Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 4th Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 5th Reminder Hey @tnull! This PR has been waiting for your review. |
…testing - Introduce `generate_block_and_insert_transactions` to manually mine a block with arbitrary transactions. - Update `bump_fee_and_broadcast` to optionally insert transactions directly into a block (bypassing the mempool) when `is_insert_block` is true. - Add integration test to insert the original (pre-RBF) transaction into a block instead of the RBF, to cover scenarios where the original transaction is confirmed and the RBF is not.
This test simulates multiple RBF transactions, randomly confirms one, then simulates a reorg and confirms another at random. This ensures the wallet correctly handles cases where any RBF transaction may be confirmed after
…oval Pending payments that become invalid or are removed from the mempool are now marked as failed. RBF tests were updated to check that only confirmed transactions succeed and all others are failed.only the actually confirmed transactions have the correct
569cebe
to
c635cb4
Compare
🔔 6th Reminder Hey @tnull! This PR has been waiting for your review. |
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.
Excuse the delay here!
(node_a, node_b) | ||
} | ||
|
||
pub(crate) fn new_node(chain_source: &TestChainSource, anchor_channels: bool) -> TestNode { |
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.
The change from the config_node
macro to this method makes sense to me, but maybe we want to align names here?
Should this rather be renamed to setup_node
, while renaming the current setup_node
to setup_node_from_config
?
use bitcoin::hashes::hex::FromHex; | ||
use bitcoin::hashes::sha256::Hash as Sha256; | ||
use bitcoin::hashes::Hash; | ||
use bitcoin::hashes::{hex::FromHex, Hash}; |
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.
nit: Please group imports at the module level.
let address = bitcoind.new_address().expect("failed to get new address"); | ||
|
||
let request_block_template = | ||
corepc_node::TemplateRequest { rules: vec![electrsd::corepc_node::TemplateRules::Segwit] }; |
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.
nit: Please avoid the full type qualifiers, rather import the types via use
above.
node | ||
} | ||
|
||
pub(crate) fn generate_block_and_insert_transactions<E: ElectrumApi>( |
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.
I wonder if it would be simpler to
- Put the RBF into the mempool
- Sync the wallet so it registers
- Mine a block
- Call Bitcoin Core's
invalidateblock
to discard the tip and the RBF - Put the original TX into the mempool
- Sync the wallet again
If I'm not mistaken that would save us all of this boilerplate?
let id = failed_payment.id; | ||
let payment_update = PaymentDetailsUpdate { | ||
id, | ||
status: Some(PaymentStatus::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.
Hmm, not convinced we should do this, as on a semantic level there is no such thing as a 'failed' onchain payment. Valid transactions are forever pending as they could always be rebroadcasted even after being evicted from the local mempool. That is why we currently only track them as Pending
or Confirmed
(once they are irrevocably confirmed).
Pending payments that are invalidated or removed from the mempool are now marked as failed. Currently, “failed” simply means the transaction is no longer in BDK’s canonical transaction list while still pending in our DB. It could later return to a pending or confirmed state if it reappears. More precise context for these “failed” states will be possible once bdk_wallet#257 is merged, as that adds richer uncanonica transaction status details.
Additional improvements:
generate_block_and_insert_transactions
to mine blocks with chosen transactions, enabling tests where either the original or a replacement transaction is confirmed.new_node
helper for clarity.Note: This PR partially addresses issue #452. I believe it does not conflict with PR #628, as this change is more general. It handles any transaction that is no longer canonical in BDK, not just RBF replacements. This fixes the problem where such transactions would remain pending indefinitely due to lack of further updates, by marking them as failed. PR #628, on the other hand, specifically handles RBFs initiated by the node.