Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor #474

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

Conversation

TheBlueMatt
Copy link
Collaborator

This is important for a number of reasons:

  • Firstly, I hit this trying to implement rescan in the demo
    bitcoinrpc client - if individual ChannelMonitors are out of
    sync with each other, we cannot add them all into a
    ManyChannelMonitor together and then rescan, but need to rescan
    them individually without having to do a bunch of manual work.
    Of the three return values in ChannelMonitor::block_connected,
    only the HTLCsource stuff that is moved here makes no sense to
    be exposed to the user.
  • Secondly, the logic currently in ManyChannelMonitor cannot be
    reproduced by the user! HTLCSource is deliberately an opaque
    type but we use its data to decide which things to keep when
    inserting into the HashMap. This would prevent a user from
    properly implementing a replacement ManyChannelMonitor, which is
    unacceptable.
  • Finally, by moving the tracking into ChannelMonitor, we can
    serialize them out, which prevents us from forgetting them when
    loading from disk, though there are still other races which need
    to be handled to make this fully safe (see TODOs in
    ChannelManager).

This is safe as no two entries can have the same HTLCSource across
different channels (or, if they did, it would be a rather serious
bug), though note that, IIRC, when this code was added, the
HTLCSource field in the values was not present.

We also take this opportunity to rename the fetch function to match
our other event interfaces, makaing it clear that by calling the
function the set of HTLCUpdates will also be cleared.

CC @ariard. I dont think this should conflict too much with your existing work, as its a pretty straightforward on-its-own change.

@TheBlueMatt TheBlueMatt force-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2a Compare February 4, 2020 05:25
@ariard
Copy link

ariard commented Feb 4, 2020

Thanks for tackling this, IIRC when I did implement first htlc updates we agreed than the API was temporary so this should be a good improvement.

Conflicts are quite minor that's fine.

@TheBlueMatt
Copy link
Collaborator Author

TheBlueMatt commented Feb 4, 2020 via email

Copy link

@ariard ariard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO a good occasion to add more reorg test coverage.

@@ -628,6 +593,8 @@ pub struct ChannelMonitor<ChanSigner: ChannelKeys> {

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

pending_htlcs_updated: HashMap<PaymentHash, Vec<(HTLCSource, Option<PaymentPreimage>)>>,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Buffer of reorg-safe(ANTI_REORG_DELAY) solved htlcs waiting to be passed upstream to the off-chain state machine. Note hash of HTLC may be duplicated on the network the real_id of HTLC should be short_channel_id+htlc_id+payment_hash".

@@ -2362,7 +2358,35 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
}
}

fn block_connected(&mut self, txn_matched: &[&Transaction], height: u32, block_hash: &Sha256dHash, broadcaster: &BroadcasterInterface, fee_estimator: &FeeEstimator)-> (Vec<(Sha256dHash, Vec<TxOut>)>, Vec<SpendableOutputDescriptor>, Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)>) {
fn append_htlc_updated(&mut self, mut htlc_updated_infos: Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)>) {
// ChannelManager will just need to fetch pending_htlcs_updated and pass state backward
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update comment.

hash_map::Entry::Occupied(mut e) => {
// In case of reorg we may have htlc outputs solved in a different way so
// we prefer to keep claims but don't store duplicate updates for a given
// (payment_hash, HTLCSource) pair.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm in fact comment isn't that much accurate, if we assume ANTI_REORG_DELAY is safe, the only reason we have this logic is because block-rescan else we shouldn't get the update thanks to onchain_events_waiting_threshold_conf.

// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If HTLC is already solved...and we have preimage don't update...if we have a timeout update...if hash1 == hash2 keep hash1 solution"

@@ -2560,7 +2581,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
},
OnchainEvent::HTLCUpdate { htlc_update } => {
log_trace!(self, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0));
htlc_updated.push((htlc_update.0, None, htlc_update.1));
self.append_htlc_updated(vec![(htlc_update.0, None, htlc_update.1)]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The buffer onchain_events_waiting_threshold_conf is design such than it should be reorg-safe to pass unfriendly state backward, but I think we don't test the first-seeing-timeout-but-then-a-preimage case (check test pair test_htlc_on_chain_success/test_htlc_on_chain_timeout). If so would be great to add test.

@TheBlueMatt TheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt TheBlueMatt force-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0 Compare February 19, 2020 05:21
@TheBlueMatt
Copy link
Collaborator Author

Right, good points - as you pointed out having an HashMap for it is now completely useless, so I just dropped that whole complexity and added a (somewhat) basic test. Let me know if this solved all your comments.

Copy link

@ariard ariard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code is good but I think some new tests are redundant

}

header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected(&Block { header, txdata: claim_txn }, CHAN_CONFIRM_DEPTH + 1);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no connect just one block here ? should be enough

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do only connect one block here? We just connect it at height CHAN_CONFIRM_DEPTH + 1.

} else {
// Confirm the timeout tx and check that we fail the HTLC backwards
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected_checked(&header, CHAN_CONFIRM_DEPTH + ANTI_REORG_DELAY, &vec![], &[0; 0]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not connect ANTI_REORG_DELAY blocks here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already connected ANTI_REORG_DELAY - 1 blocks above, this is just the last one to get it to ANTI_REORG_DELAY.

do_test_onchain_htlc_reorg(true, true);
}
#[test]
fn test_onchain_htlc_timeout_delay_local_commitment() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both local/remote cases are already covered by test_htlc_on_chain_timeout?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, probably. I still like checking that the one-block-difference is exactly the same but does something different. If you really feel strongly I can remove it.

@TheBlueMatt TheBlueMatt force-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283 Compare February 21, 2020 00:44
This is important for a number of reasons:
 * Firstly, I hit this trying to implement rescan in the demo
   bitcoinrpc client - if individual ChannelMonitors are out of
   sync with each other, we cannot add them all into a
   ManyChannelMonitor together and then rescan, but need to rescan
   them individually without having to do a bunch of manual work.
   Of the three return values in ChannelMonitor::block_connected,
   only the HTLCsource stuff that is moved here makes no sense to
   be exposed to the user.
 * Secondly, the logic currently in ManyChannelMonitor cannot be
   reproduced by the user! HTLCSource is deliberately an opaque
   type but we use its data to decide which things to keep when
   inserting into the HashMap. This would prevent a user from
   properly implementing a replacement ManyChannelMonitor, which is
   unacceptable.
 * Finally, by moving the tracking into ChannelMonitor, we can
   serialize them out, which prevents us from forgetting them when
   loading from disk, though there are still other races which need
   to be handled to make this fully safe (see TODOs in
   ChannelManager).

This is safe as no two entries can have the same HTLCSource across
different channels (or, if they did, it would be a rather serious
bug), though note that, IIRC, when this code was added, the
HTLCSource field in the values was not present.

We also take this opportunity to rename the fetch function to match
our other event interfaces, makaing it clear that by calling the
function the set of HTLCUpdates will also be cleared.
@TheBlueMatt TheBlueMatt force-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360 Compare February 21, 2020 01:32
@ariard
Copy link

ariard commented Feb 21, 2020

ACK d296360

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants