BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt
, '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

BOLT5: fix claim backward revoked htlc success - #322

Closed
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success
Closed

BOLT5: fix claim backward revoked htlc success#322
ariard wants to merge 2 commits into
lightningdevkit:masterfrom
ariard:2019-03-fix-claim-backward-revoked-htlc-success

Conversation

@ariard

Copy link
Copy Markdown

Seems we forget to implement the case where a revoked HTLC-success is broadcast, providing a preimage to a still valid in backward channel HTLC. We need to extract it, map it to source still available in prev/current_remote_commitment_tx and pass htlc_update upstream.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I believe this was deliberately left out - if its the case it implies we can (and will) claim the HTLC-Transaction once we pass it to check_spend_remote_htlc. While we could double-claim the funds here, guessing which HTLC(s) to claim based on the hash+value in a (semi-)unrelated data datastructure feels super awkward.

Comment threadsrc/ln/channelmonitor.rs Outdated
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we move these checks into the if Some(...) = self.remote_claimable_outpoints.get(&input.previous_output.txid) conditional below? They don't make any sense in the spends-local-commitment-transaction branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it where it is but used $local_tx to avoid iterating twice on htlc_outputs

@ariard

Copy link
Copy Markdown
Author

There we will claim the revoked HTLC-success tx via check_spend_remote_htlc, but we also have to fulfill the BOLT 5 requirement to extract preimage for a still-valid HTLC in backward channel if don't have yet this one. Which are the funds we risk to double-claim ?

Agree that's a bit hacky, but other way was to avoid pruning of offered HTLC outputs sources from remote_claimable_outpoints at provide_secret and keeping them forever. Seems to be a tradeoff between hash collision (but at least we claim something backward) and storage.

@TheBlueMatt

TheBlueMatt commented Mar 23, 2019 via email

Copy link
Copy Markdown
Collaborator

@ariard

ariard commented Mar 23, 2019

Copy link
Copy Markdown
Author

Hmmm I agree with the BOLT there, why fail back HTLC on our forwarded-from counterparty whereas you get the opportunity to succeed the payment ? On a general network view, we just improve the delivery rate of payment, avoid burden for origin node to recompute route and processing nodes to deal with new onions.

IMO strictly speaking it's isn't double claim of same funds because they result from 2 different funding_outpoint ?

Will split and do a big comment, explaining the tradeoff, and I think taking the could-be "wrong" source way is better than eternal temporary storage (the first is highly-unlikely, the second always certain).

Edit: we may prune offered HTLC sources after expiration or fulfillment, but it may add a reasonable amount of processing for watchtower to do so

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I guess thinking about this more because the check is "same hash and value" its no different from any other claim - we cannot identify the sender is "the correct one and not someone trying to deanonymize a payment" if the value and hash are the same. My point about the BOLT was that its dumb for the BOLT to tell us we MUST do this, though recommending we MAY do this would seem acceptable, it should be up to the client how much they want to double-claim funds related to the same payment.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Will take the first commit as #325, dunno about the test, though.

header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[0], node_txn[1].clone());
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()] }, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ugh, I really hate this test - at this point nodes[1] has a pending fail_backwards of the HTLC and its just waiting on a future process_pending_htlc_forwards() call. We relay on claims bypassing the pending_forwards stuff to skip the fail and actually fulfill. At some point I'd kinda like to batch claims in addition to fails, which would break this test. I'd kinda prefer we just leave the test change until #305 lands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Got your point, agree to wait on this

// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {
log_claim!($tx_info, $local_tx, htlc_output, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think you only want to do this in the payment_data case.

if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
} else if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This if block can never be hit (and should only run if the previous block failed to set payment_data).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, in fact even fail to build a test to this, but wasn't 100% sure it can't be hit

// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash));
} else if !$local_tx {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note that we still want to hit the last else even if !$local_tx as long as payment_data is still none.

@ariard

Copy link
Copy Markdown
Author

Anyway, now tracking has landed, will rewrite a test on this point as part of BOLT 5 review

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

@ariard@TheBlueMatt