Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace
, '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

Randomize user_channel_id for inbound channels - #1790

Merged
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization
Nov 15, 2022
Merged

Randomize user_channel_id for inbound channels#1790
TheBlueMatt merged 3 commits into
lightningdevkit:mainfrom
tnull:2022-10-inbound-user-channel-id-randomization

Conversation

@tnull

Copy link
Copy Markdown
Contributor

Previously, all inbound channels defaulted to a user_channel_id of 0, which didn't allow for them being discerned on that basis. Here, we simply randomize the identifier to fix this and enable the use of user_channel_id as a true identifier for channels (assuming an equally reasonable value is chosen for outbound channels and given upon create_channel()).

@codecov-commenter

codecov-commenter commented Oct 21, 2022

Copy link
Copy Markdown

Codecov Report

Base: 90.77% // Head: 91.67% // Increases project coverage by +0.90% 🎉

Coverage data is based on head (a2616a9) compared to base (505102d).
Patch coverage: 67.90% of modified lines in pull request are covered.

❗ Current head a2616a9 differs from pull request most recent head d458fa8. Consider uploading reports for the commit d458fa8 to get more accurate results

Additional details and impacted files
@@ Coverage Diff @@## main #1790 +/- ##
==========================================
+ Coverage 90.77% 91.67% +0.90% 
==========================================
Files 87 89 +2 Lines 47595 55343 +7748 Branches 47595 55343 +7748 ==========================================
+ Hits 43204 50737 +7533 - Misses 4391 4606 +215 
Impacted FilesCoverage Δ
lightning/src/ln/channelmanager.rs88.39% <51.92%> (+2.98%)⬆️
lightning/src/util/events.rs38.66% <90.00%> (+1.04%)⬆️
lightning/src/ln/channel.rs90.35% <100.00%> (+1.64%)⬆️
lightning/src/ln/functional_test_utils.rs93.46% <100.00%> (ø)
lightning/src/util/ser.rs93.64% <100.00%> (+1.97%)⬆️
lightning/src/util/ser_macros.rs89.09% <100.00%> (+0.28%)⬆️
lightning/src/chain/mod.rs66.66% <0.00%> (-1.52%)⬇️
lightning/src/ln/monitor_tests.rs99.44% <0.00%> (-0.12%)⬇️
lightning/src/lib.rs100.00% <0.00%> (ø)
lightning/src/ln/reorg_tests.rs100.00% <0.00%> (ø)
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hmm, this is a bit awkward, given we require the user to pass an ID for outbound channel, but it gets picked at random for inbound ones? We run some risk of colliding, even if its not super high. Ideally we'd increment rather than randomize, and keep track of the last one for outbounds, if we want to do this. Do note that users can always set their own incrementing IDs if they do manual channel acceptance.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh, no, I guess incrementing is inherintly race-y, we can't do that. Ugh, I guess we can randomize, but I feel really bad doing something that users may rely on (randomization being unique always) and then having it randomly fail. If its okay with your use-case it'd be nice to just have you rely on the manual acceptance, rather than relying on upstream.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature? Especially since currently the default behavior to have a collision in ~50% of cases? Also, correct me if I'm wrong, but I couldn't find any part of the code where we would rely on the 0 magic value, and hopefully no one else does, too?

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions. That said, if we were to have a null default value, this should probably be an Option<u64> rather than having a 0 magic value.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Hm, but are we really worried about a collision in an 64-bit identifier space for a non security critical feature?

I would definitely call it "security critical", having users get confused between different channels definitely sounds like a potentially critical issue. That said, maybe we don't need to care? Mentally, my model is always (a) 32-bit -> dont use, (b) 64-bit -> fine for counters, even if a counterparty can cause you to increment it at a high rate, which they can here, (c) 128-bit -> fine if you dont want to care about collisions, (d) 256-bit -> just do it. But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

So I'd argue randomization is just a plain improvement over the status quo, even though you are correct, there is a negligible chance of collisions.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

@tnull

tnull commented Oct 21, 2022

Copy link
Copy Markdown
ContributorAuthor

But, in this case, 64-bit random numbers - if a counterparty is generating random inbound channels to try to cause collision, after 100million channels you still only have a ~0.02-0.03% chance of collisions. Its not impossible, but very very low, maybe sufficient that it will never happen in prod anywhere.

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

I think this is the wrong way of thinking about it - if there is a low-but-possible-edge-case of collisions, we'd rather cause collisions to be the "norm" so that users either handle it or avoid it via manual acceptance. Super rare bugs that could cause issues are worse than making it the "norm" where devs will see it during testing.

It's not as if we force users to supply their own identifiers, we simply notify them in the docs that the identifiers are all 0.
I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

@TheBlueMatt

TheBlueMatt commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

Right, and it's not as if channel creation is a high-frequency action for which we blast through 100million events.

If there's an attack with duplicate IDs, it absolutely is - a node can send open_channel messages really fast :)

I'd argue the likelihood of a developer not reading the docs and just running into a bug in production because all inbound having the same identifier is much, much higher that having and actual collision.

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest. Collisions you'll never hit in testing.

@tnull

Copy link
Copy Markdown
ContributorAuthor

I don't understand this - if a user relies on the IDs being unique, they won't just hit it in prod, they'll hit it in their third day of testing, at the latest.

That's quite optimistic. To me that sounds like the kind of bug that could easily slip through eventually. Also we still could have a note there explaining the risk and that users should roll their own IDs if possible, just that the default would be just a bit saner.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Oh? Getting a second inbound channel from the LSP seems like something that any dev working with an LSP would test?

In any case, maybe all of this just means our user_channel_id abstraction makes no sense. We had a similar one for payments but ended up ripping it out entirely (and eventually, basically, replacing it with PaymentId). I wonder if we shouldn't try to do something similar here - rip out the fields and have some LDK-provided 32-byte value, or an LDK-provided counter, or...?

@G8XSU

G8XSU commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

I would feel much more comfortable here if its something normally used as unique identifier in high scale systems, for example something like uuid which is 128-bit and regularly used as key in database systems at very high scale.

@tnull

tnull commented Oct 24, 2022

Copy link
Copy Markdown
ContributorAuthor

Alright, so why not simply switch the user_channel_id to a u128 and randomize it? This would allow users to fit a UUID in there if the wanted, and to quote Matt:

(c) 128-bit -> fine if you dont want to care about collisions

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I'm fine with that. Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

@tnull

tnull commented Oct 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Sadly its not "trivially backwards compatible" because TLV reads must read the full expected byte count, so we'll need to write a separate "high bits" TLV.

Yeah, figured so too, which is why there is no mention of "trivially backwards compatible" in above post anymore 😁

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch 2 times, most recently from 43403bf to 1150480CompareOctober 25, 2022 09:33
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Ah, I was responding to the email/initial copy, which was edited out from under me :)

@valentinewallace

Copy link
Copy Markdown
Contributor

I think this fixes fuzz CI:

diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 7edba558..322b1480 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -404,7 +404,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 1150480 to eacf4efCompareOctober 26, 2022 15:48
@tnull

Copy link
Copy Markdown
ContributorAuthor

I think this fixes fuzz CI:
...

Thanks, I should start to remember that. 🙏

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM after squash

Comment threadlightning/src/util/events.rs
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from eacf4ef to d26e4b5CompareOctober 26, 2022 16:49
@tnull

Copy link
Copy Markdown
ContributorAuthor

Squashed commits.

valentinewallace
valentinewallace previously approved these changes Oct 27, 2022
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/ln/channel.rs Outdated
Comment threadlightning/src/util/events.rs Outdated
valentinewallace
valentinewallace previously approved these changes Oct 28, 2022
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

LGTM, feel free to squash.

}
}

impl_writeable_primitive!(u128, 16);

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.

Oops, so we should remove this - note that you broke backwards compat on the ChannelDetails serialization. It'd be very nice to be able to avoid breaking out the macro for this, though...Maybe we define a new macro read type that's, like, custom_adapter and has a conversion method? Ugh...

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.

LMK if you want me to take a look at this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Working on it, will give an update ASAP. Still not sure if it won't be easier to break the macro though.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

As discussed offline I explored a number of approaches, e.g., utilizing a custom adapter in conjunction with handing through a decode_custom_tlv function. They seemed to be almost working on the decoding end (but don't really), and the encoding end is even trickier. Open for any suggestions how to move forward on this, otherwise I now broke the macro and now do custom de/ser as of 3a7bd26.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 80699d9 to d06e17bCompareNovember 8, 2022 09:05
@tnull

tnull commented Nov 8, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased on main after #1743 was merged.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d06e17b to 5093ebaCompareNovember 8, 2022 09:25
@tnulltnull added this to the 0.0.113 milestone Nov 8, 2022
Comment threadlightning/src/util/events.rs
Comment threadlightning/src/ln/channel.rs Outdated

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

Okay, thought about it more, I don't think we should try to shove the whole split-int thing into the broader impl_writeable_tlv_based macro, but we I think there's at least one option for cleaning this up below.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 3a7bd26 to 8899a83CompareNovember 15, 2022 13:58
We introduce a new macro that inits and reads tlv fields and DRY up
`impl_writeable_tlv_based` and other macros.
Previously, all inbound channels defaulted to a `user_channel_id` of 0,
which didn't allow for them being discerned on that basis. Here, we
simply randomize the identifier to fix this and enable the use of
`user_channel_id` as a true identifier for channels (assuming an equally
reasonable value is chosen for outbound channels and given upon
`create_channel()`).
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 8899a83 to a2616a9CompareNovember 15, 2022 14:10
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from 7371a52 to d458fa8CompareNovember 15, 2022 19:14
@tnull

Copy link
Copy Markdown
ContributorAuthor

There are still a handful of incorrect docs in events.rs that still says user_channel_id will be 0 for inbound channels. Otherwise this looks basically good to me.

Whoops, updated the docs.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash, IMO.

We increase the `user_channel_id` type from `u64` to `u128`. In order to
maintain backwards compatibility, we have to de-/serialize it as two
separate `u64`s in `Event` as well as in the `Channel` itself.
@tnull
tnullforce-pushed the 2022-10-inbound-user-channel-id-randomization branch from d458fa8 to dc3ff54CompareNovember 15, 2022 19:41
@tnull

tnull commented Nov 15, 2022

Copy link
Copy Markdown
ContributorAuthor

Squashed without further changes.

Comment threadlightning/src/util/events.rs
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be 0 for an inbound channel.
/// `user_channel_id` will be randomized for an inbound channel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but could say that the version it starts being randomized in

@tnulltnullNov 16, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Will make sure to include it in a follow-up, probably when having a look at #1800!

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in #1855.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Gonna merge, will let @tnull tackle #1790 (comment) in a followup if desired.

@TheBlueMatt
TheBlueMatt merged commit 8d8ee55 into lightningdevkit:mainNov 15, 2022
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.

5 participants

@tnull@codecov-commenter@TheBlueMatt@G8XSU@valentinewallace