Skip to content

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

@tnull@TheBlueMatt@codecov-commenter@jkczyz@jurvis@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add random 'shadow route' CLTV delta offsets to improve privacy. by tnull · Pull Request #1286 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

@tnull@TheBlueMatt@codecov-commenter@jkczyz@jurvis@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add random 'shadow route' CLTV delta offsets to improve privacy. by tnull · Pull Request #1286 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

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

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

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

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

@tnull@TheBlueMatt@codecov-commenter@jkczyz@jurvis@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add random 'shadow route' CLTV delta offsets to improve privacy. by tnull · Pull Request #1286 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

@tnull@TheBlueMatt@codecov-commenter@jkczyz@jurvis@ariard
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add random 'shadow route' CLTV delta offsets to improve privacy. by tnull · Pull Request #1286 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

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

Add random 'shadow route' CLTV delta offsets to improve privacy. - #1286

Merged
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets
Mar 9, 2022
Merged

Add random 'shadow route' CLTV delta offsets to improve privacy.#1286
jkczyz merged 1 commit into
lightningdevkit:mainfrom
tnull:add_random_cltv_offsets

Conversation

@tnull

Copy link
Copy Markdown
Contributor

This PR is an attempt to address #158.

In order to generate a plausible CLTV delta offset, a three-hop random walk starting from the payee is conducted and cltv_expiry_delta values are aggregated. This offset is then added to all but the final hop of the selected paths (since the last hop knows anyways that it is the recipient).

Tests are currently failing since they of course do not expect the randomly added offset. I'll fix them when someone had a look at the implementation itself.

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

Sorry for the delay on this one! A few comments, don't worry about the failing tests for now, I can help out with those if needed.

Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 87fc7d4 to ea0ad9dCompareFebruary 4, 2022 14:08
@tnull

tnull commented Feb 4, 2022

Copy link
Copy Markdown
ContributorAuthor

Addressed comments and rebased off a1fedea.

Comment threadlightning/src/routing/router.rs
Comment threadlightning/src/util/mod.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 251374f to f6793abCompareFebruary 7, 2022 12:35
Comment threadlightning/src/routing/router.rs Outdated
if let Some(random_index) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len()) {
if let Some(random_channel_id) = cur_node.channels.get(random_index) {
if let Some(random_channel) = network_channels.get(random_channel_id) {
if random_channel.node_one == cur_node_id {

@ariardariardFeb 14, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One improvement, I think if the randomly selected next hop is actually part of the extended payment path you could pick up another hop to pursue the random walk. What we would like to avoid is the random walk rolling back on the actually drawn out path. Let's say you have A -> B -> C -> D and you sort the random walk D -> C -> B -> A thus a shadow offset partially overlapping with the real cltv deltas. I would say it's a low-probability phenomena but the last publicly known node might not have numerous public channels opened for routing.

That said, it might not be worthy the effort at that point.

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.

Thank you very much for having a look at this! Could you elaborate why we wouldn't want the shadow route to overlap the actual path? I see this only as a problem if we assume an on-path adversary that can say for sure that it wasn't part of the actual payment path and hence might be able to reduce the number of candidate routes. However, if we exclude overlapping paths from the beginning, we might reduce the randomness from the getgo, making any adversary's life easier?

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.

I'm not sure if its worth the effort to implement this, but I see a few concerns with overlapping paths. eg if you're an attacker and the CLTV deltas only line up with one path, and it overlaps/backtraces, you know for sure that that is shadow route, because the normal routing algorithm won't exhibit that behavior.

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.

Hum, I think it may not be worth to rule out any overlap, since this could indeed reduce the variety of potential random walks quite a bit. But maybe it would be enough to ensure that we do not immediately loop back on the actual path. And in the case this excludes the only public edge we could fall back to a 'synthetic' random offset, just as in the case for fully private paths.

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.

Yea, I'll leave it up to you. I don't care too much because so many nodes use the same CLTV delta that its probably all the same in practice, but if its easy to avoid the same-node-backtrack then feel free to add it.

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.

I think I'm leaning on the side of leaving this as-is for now and potentially having another look at it in a potential follow-up PR. We might want to have a follow-up anyways, for example in order to introduce some kind of value obfuscation, as noted in #158.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah really up to you, fine to leave it as a follow-up.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Looks like this needs rebase, sorry about that.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 3794bf6 to 803348fCompareFebruary 16, 2022 08:17
@tnull

Copy link
Copy Markdown
ContributorAuthor

Looks like this needs rebase, sorry about that.

Rebased.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 803348f to 4e9cb21CompareFebruary 18, 2022 13:47

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

I'm basically happy with this. There's some compilation errors due to new tests that weren't updated on rebase and the benchmark-only code needs updating, I believe. I'm happy to help resolve any test failures left after that.

Comment threadlightning/src/routing/router.rs
@jkczyz
jkczyz self-requested a review February 18, 2022 18:57
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from ea06afb to 28cebe1CompareFebruary 21, 2022 12:58
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 78e9dcd to a20cd5cCompareFebruary 22, 2022 10:19
@tnull

tnull commented Feb 22, 2022

Copy link
Copy Markdown
ContributorAuthor

To make the router tests pass, maybe it'd be best to split get_route into two methods, one that gets the route and one that applies the CLTV deltas so that you don't have to update all the tests.

Good idea, especially given that get_route is ~1000 lines already. I now split adding the offset to its dedicated add_random_cltv_offset method and added a test.

For the time being I left random_seed_bytes as a parameter to get_route instead of removing it and all the changes to the test etc. I think it could be used to implement the 'real' random shuffle in Step (6). If that is wanted I can add this to this PR or open a follow-up.

@codecov-commenter

codecov-commenter commented Feb 22, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1286 (e92b5a7) into main (af99a94) will decrease coverage by 0.14%.
The diff coverage is 95.26%.

Impacted file tree graph

@@ Coverage Diff @@## main #1286 +/- ##
==========================================
- Coverage 90.75% 90.60% -0.15% 
==========================================
Files 72 72 Lines 42254 40324 -1930 ==========================================
- Hits 38347 36536 -1811 + Misses 3907 3788 -119 
Impacted FilesCoverage Δ
lightning-invoice/src/utils.rs86.92% <58.33%> (-1.22%)⬇️
lightning/src/ln/channelmanager.rs84.61% <87.50%> (-1.52%)⬇️
lightning/src/routing/network_graph.rs89.52% <88.88%> (-0.01%)⬇️
lightning/src/routing/router.rs92.39% <96.15%> (+0.29%)⬆️
lightning/src/routing/scoring.rs94.29% <96.96%> (-1.13%)⬇️
lightning-background-processor/src/lib.rs93.10% <100.00%> (+0.05%)⬆️
lightning/src/ln/functional_test_utils.rs95.32% <100.00%> (+0.03%)⬆️
lightning/src/ln/functional_tests.rs97.13% <100.00%> (+0.36%)⬆️
lightning/src/ln/onion_route_tests.rs97.62% <100.00%> (ø)
lightning/src/ln/payment_tests.rs99.15% <100.00%> (+<0.01%)⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a73449...e92b5a7. Read the comment docs.

Comment threadlightning/src/routing/router.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from c0657fd to 67137c6CompareFebruary 22, 2022 17:57
@tnull

Copy link
Copy Markdown
ContributorAuthor

Feel free to squash the commits down to one or two, note that we'd ideally like to ensure all code compiles and all tests pass after each commit.

Sure sure, just wanted to avoid squashing too early. Rebased and squashed with 67137c6.

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

Some style nits and minor test comments.

Comment threadfuzz/src/router.rs Outdated
Comment threadfuzz/src/full_stack.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

As is the lock is held through find_route which we maybe don't want. Instead, we'll need an explicit {} scope to tell the compiler where to drop the locked_random_seed_bytes

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

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.

I now went the scoping route with 386a0b5. However, I agree that introducing a Randomness interface (not only here) would be a nicer abstraction than pushing around arrays with random_seed_bytes. Possibly a good follow-up PR?

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.

I suppose we could, yea, agree we'd want to do it in a followup, though. I'm a little hesitant to replace a trivial parameter with a full interface, though, as it just seems like overkill, but we can discuss that later.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/scoring.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
) -> Result<Route, LightningError> {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
*locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer, &locked_random_seed_bytes.clone())

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.

Should we consider parameterizing DefaultRouter by K: Deref where K::Target: KeysInterface and call get_secure_random_bytes each time? I suppose the interface is too broad, but I wonder if a narrower Randomness interface is warranted. No strong opinion here, but it would avoid the mutex.

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
Comment threadlightning/src/ln/payment_tests.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated

@ariardariard left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good with the new approach of only offsetting the final hop.
(Thought the old one had the benefit to also mask the sender topology as the intermediate node couldn't deduce from their CLTV deltas the distance from sender, I believe)

Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 2 times, most recently from 386a0b5 to 01bccc4CompareFebruary 24, 2022 16:06
@tnull

Copy link
Copy Markdown
ContributorAuthor

Thanks for all the great feedback, I'll take it into account going forward!

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from 22b4eeb to ee58764CompareFebruary 25, 2022 11:51
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
// Limit the offset so we never exceed the max_total_cltv_expiry_delta
let max_path_offset = payment_params.max_total_cltv_expiry_delta
.checked_sub(path.iter().map(|h| h.cltv_expiry_delta).sum())
.unwrap_or(shadow_ctlv_expiry_delta_offset);

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.

Shouldn't this be unwrap_or(0)? If our path expiry is more than the max already we probably shouldn't add an offset at all? Also, were we planning on limiting the total CLTV during the get_route run to give us a little headroom here?

@tnulltnullFeb 26, 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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail. If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none. But it can of course also default to 0, I have no strong preference here.

As for the considering the offset in get_route: after looking at the data I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate. However, if we still want to take some of the offset into account, I can of course go back to, for example, subtracting 2*40 or 2*144 from the limit during path finding.

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.

Well my thinking here was that since we limit the route overall with max_total_cltv_expiry_delta, this subtraction should never fail.

Right, sorry, indeed.

If however something unexpected were to happen and it would fail, we might be better off to default to provide some privacy (capped at 3*144), instead of none.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?

As for the considering the offset in get_route: #1286 (comment) I found that there were basically no cases in which there was not ample room to add the offset and hence thought it would be cleaner not to interfere with pathfinding and keep the two methods entirely separate.

Hmm, I feel like that's a better reason to interfere than to not interfere.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

@tnulltnullFeb 27, 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.

🤷‍♂️ I guess we shouldn't ever violate the user-provided value ever?
...
Hmm, I feel like that's a better reason to interfere than to not interfere.

Alright, both addressed by f7be9da.

I'm still a bit confused by this hunk here - why do we do the subtraction at max_path_offset.checked_sub(max_path_offset.wrapping_rem(40))?

The difference of the path total to max_total_cltv_expiry_delta may be a pretty uncommon value, which could result in an implausible shadow route offset. The thinking here therefore was to choose the path offset to be the closest multiple of 40 to increase plausibility. I now had another look at this part and added a comment to make this more transparent in f7be9da.

@tnull
tnullforce-pushed the add_random_cltv_offsets branch from f7be9da to 773e76bCompareFebruary 27, 2022 09:07
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning-invoice/src/utils.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 09c262f to a6f241cCompareFebruary 28, 2022 21:28
@tnull

Copy link
Copy Markdown
ContributorAuthor

Rebased and squashed once more.

Comment threadlightning/src/routing/router.rs Outdated
logger: L, scorer: &S
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
logger: L, scorer: &S, random_seed_bytes: &[u8; 32]

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.

random_seed_bytes is not needed here since it is only used in add_random_cltv_offset.

@tnulltnullMar 1, 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.

This is correct. If however possible, I’d like to leave it in get_route for now and use it to implement the 'real random shuffle' TODO in a follow-up PR. I now prefixed it with an underscore to disable the warning, but let me know if you'd rather keep it out of get_route altogether in this PR.

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

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.

hey @tnull is it okay if I take a stab at implementing the real random shuffle? I believe it may be related to this issue #869

Ah, thank you for the interest! This issue indeed seems related. However, I’m already mostly done with a first draft and plan to open a PR in the next few days.

Comment threadfuzz/src/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch from a6f241c to f5b1b46CompareMarch 1, 2022 08:45
Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
Comment on lines +1614 to +1616
max_path_offset = max_path_offset
.wrapping_sub(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA))
.max(max_path_offset.wrapping_rem(MEDIAN_HOP_CLTV_EXPIRY_DELTA));
shadow_ctlv_expiry_delta_offset = cmp::min(shadow_ctlv_expiry_delta_offset, max_path_offset);

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.

If the subtraction wraps, shadow_ctlv_expiry_delta_offset would not likely change, IIUC. Could it then possibly exceed payment_params.max_total_cltv_expiry_delta for small values?

@tnulltnullMar 3, 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.

Hum, I'm not entirely sure if I'm understanding you correctly. The wrapping_sub should actually never wrap due to x >= x mod y. I made this a bit more explicit by using saturating_sub in 0b777cb.
Moreover, the line prior ensures that max_path_offset is never larger than max_total_cltv_expiry_delta and all changes from there only ever would reduce it further.

Comment threadlightning/src/routing/router.rs Outdated
Comment threadlightning/src/routing/router.rs Outdated
@tnull
tnullforce-pushed the add_random_cltv_offsets branch 3 times, most recently from 0b777cb to 5731df3CompareMarch 4, 2022 12:47
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
Comment threadlightning/src/routing/router.rs Outdated
TheBlueMatt
TheBlueMatt previously approved these changes Mar 7, 2022
@jkczyzjkczyz added this to the 0.0.106 milestone Mar 7, 2022
@tnull

tnull commented Mar 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Rebased.

@jkczyz
jkczyz merged commit a50e954 into lightningdevkit:mainMar 9, 2022
@tnull
tnull deleted the add_random_cltv_offsets branch March 9, 2022 22:45
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.

6 participants

@tnull@TheBlueMatt@codecov-commenter@jkczyz@jurvis@ariard