Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor - #474

Merged
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors
Feb 21, 2020
Merged

Move pending-HTLC-updated ChannelMonitor from ManyChannelMonitor#474
TheBlueMatt merged 2 commits into
lightningdevkit:masterfrom
TheBlueMatt:2020-02-htlc-updated-in-monitors

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from ff12ea9 to 3946b2aCompareFebruary 4, 2020 05:25
@ariard

Copy link
Copy Markdown

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

TheBlueMatt commented Feb 4, 2020 via email

Copy link
Copy Markdown
CollaboratorAuthor

@ariardariard left a comment

Copy link
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated

payment_preimages: HashMap<PaymentHash, PaymentPreimage>,

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

Copy link
Copy Markdown

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".

Comment threadlightning/src/ln/channelmonitor.rs Outdated

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
Copy Markdown

Choose a reason for hiding this comment

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

Update comment.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

Comment threadlightning/src/ln/channelmonitor.rs Outdated
// (payment_hash, HTLCSource) pair.
let mut existing_claim = false;
e.get_mut().retain(|htlc_data| {
if htlc.0 == htlc_data.0 {

Copy link
Copy Markdown

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"

Comment threadlightning/src/ln/channelmonitor.rs Outdated
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
Copy Markdown

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.

@TheBlueMattTheBlueMatt added this to the 0.0.10 milestone Feb 17, 2020
@TheBlueMatt
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 380c822 to 26aafd0CompareFebruary 19, 2020 05:21
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

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.

@ariardariard left a comment

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
Copy Markdown

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
Copy Markdown
CollaboratorAuthor

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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from 0d0558a to bc52283CompareFebruary 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
TheBlueMattforce-pushed the 2020-02-htlc-updated-in-monitors branch from bc52283 to d296360CompareFebruary 21, 2020 01:32
@ariard

Copy link
Copy Markdown

ACK d296360

Sign up for freeto 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

@TheBlueMatt@ariard