-
Notifications
You must be signed in to change notification settings - Fork 952
Multi-frame onion format proposal #2363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
aac29da
to
ad4aa42
Compare
Depends on whether EDIT: If we include |
I want to apply this rework for an independent PR, so I'm appending the code to make the multi-hop parsing EXPERIMENTAL only so we can merge... |
@@ -89,10 +89,14 @@ struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx, | |||
|
|||
static size_t sphinx_hop_count_frames(const struct sphinx_hop *hop) | |||
{ | |||
#if EXPERIMENTAL_FEATURES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is likely redundant with the check on line 426, since this just clamps the number of frames to 1, never triggering the failure, and probably corrupting the frame if we end up using more anyway. I suggest we just keep the check below, and restore the normal behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
77f361c
to
7fc3e2c
Compare
During my rebase I accidentally removed @rustyrussell's experimental guards. Rebased and cherry-picked it back onto the PR :-) |
For the multi-frame support we need to introduce the FRAME_SIZE parameter and I took the opportunity to fix up some of the naming. Signed-off-by: Christian Decker <decker.christian@gmail.com>
`struct sphinx_path` serves as a container for all the routing related information, with a couple of constructors that can be used for normal operation or testing (with pre-determined `session_key`). Signed-off-by: Christian Decker <decker.christian@gmail.com>
This is just taking the existing serialization code and repackaging it in a more useful form. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This was a mismatch between the go tool and this test tool so far. Just aligning the tools to allows for easier testing. Signed-off-by: Christian Decker <decker.christian@gmail.com>
Signed-off-by: Christian Decker <decker.christian@gmail.com>
Just some reorganizations and clarifications before we add the multi-frame support. Signed-off-by: Christian Decker <decker.christian@gmail.com>
Shouldn't be used directly, but really useful for testing, since we can just cram a huge payload in without having to be valid. And we don't have a TLV spec yet. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This is the actual change when constructing the multi-frame onion. It changes the rightshift, and how much filler we need to generate. It also has the realm stuffing with the number of additional frames. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This is all it takes on the read side to use multiple frames. We are overshooting the padding a bit since we can at most use 16 additional frames, but ChaCha20 is cheap. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This still only supports v0 payloads, but it is flexible enough to accomodate future payload formats as well, with the type-tag and the union holding the actual parsed data. It also better encapsulates the decoding of the former realm byte into payload type and frame count. Signed-off-by: Christian Decker <decker.christian@gmail.com>
Signed-off-by: Christian Decker <decker.christian@gmail.com>
The `runtest` command takes a JSON onion spec, creates the onion and decodes it with the provided private keys. It is fully configurable and can be used for the test-vectors in the spec. Signed-off-by: Christian Decker <decker.christian@gmail.com>
…FEATURES. This lets us merge it, since it has a nice refactor I would like to use for invoice hooks (raw payload access). Also checks that we don't give a payload too large to encode! Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Updated the PR to reflect the change in realm byte split. It now uses a 5bit + 3bit split like the recent change to lightning/bolts#593. Once that is merged we can go ahead as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a really nice cleanup! Only one weird useless loop which needs deleting.
I've suggested a few tweaks, but really they can be done as add-on PRs if you prefer, so consider it acked once that loop in pay.c is removed!
common/sphinx.c
Outdated
@@ -166,7 +165,7 @@ static bool generate_header_padding( | |||
) | |||
{ | |||
int i; | |||
u8 cipher_stream[(NUM_MAX_HOPS + 1) * hopsize]; | |||
u8 cipher_stream[(NUM_MAX_FRAMES + 1) * FRAME_SIZE]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either this change is wrong, or hopsize should not be a parameter to this function?
#define FRAME_SIZE 65 | ||
#define NUM_MAX_FRAMES 20 | ||
#define ROUTING_INFO_SIZE (FRAME_SIZE * NUM_MAX_FRAMES) | ||
#define TOTAL_PACKET_SIZE (VERSION_SIZE + PUBKEY_SIZE + HMAC_SIZE + ROUTING_INFO_SIZE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A properly marked /* BOLT #4
quote here would be illiminating and useful :)
return sp; | ||
} | ||
|
||
struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe wrap this declaratio (and defn) in #if DEVELOPER
to emphasize it? Means devtools/onion is now DEVELOPER-only, but that's probably a Good Thing?
dest[0] = hop->realm; | ||
memcpy(dest + 1, hop->payload, tal_bytelen(hop->payload)); | ||
memcpy(dest + FRAME_SIZE - HMAC_SIZE, hop->hmac, HMAC_SIZE); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird indent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird indent fixed :)
struct node_id, | ||
&ids[i])); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing this sanity check makes me uncomfortable. But why don't we simply make ids
a struct pubkey array directly, and do the pubkey_from_node_id(&ids[i], &route[i].nodeid))
here in the loop and then fail immediately like this did?
const struct short_channel_id *scid, struct amount_msat forward, | ||
u32 outgoing_cltv); | ||
/** | ||
* Add a raw payload hop to the path. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe note it's expose for testing...
memcpy(dest + FRAME_SIZE - HMAC_SIZE, hop->hmac, HMAC_SIZE); | ||
u8 num_frames = sphinx_hop_count_frames(hop); | ||
memset(dest, 0, num_frames * FRAME_SIZE); | ||
dest[0] = hop->realm | (num_frames-1) << 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
binds less than <<
but I still want more brackets here please :) Also, this is now 3 is it not?
Maybe a heap of assertions here, like assert(hop->realm < (1 << 4)
and assert(tal_bytelen(hop->payload) < sphinx_hop_count_frames(hop) * FRAME_SIZE) - HMAC_SIZE - 1)
?
|
||
enum sphinx_payload_type { | ||
SPHINX_V0_PAYLOAD = 0, | ||
SPHINX_RAW_PAYLOAD = 255, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whenever we overload values for internal usage, I always worry about leakage (someone sends us a realm 255 onion in future).
Let's make route_step.realm
and int, and use negative values for internal ones maybe? Or 256?
@@ -0,0 +1,42 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer to see this tested in a unit test rather than a devtool, though.
@@ -89,10 +89,14 @@ struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx, | |||
|
|||
static size_t sphinx_hop_count_frames(const struct sphinx_hop *hop) | |||
{ | |||
#if EXPERIMENTAL_FEATURES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
Re-added the Work in Progress tag since we need an upstream decision about which proposal should be merged. |
Looks like this should be closed in favor of the other PR; removed milestone tag anyway. |
Quoting the description from lightningnetwork/lightning-onion#31:
For a payload of length <= 32:
For payloads > 32 and <= 162 for example:
In other words we use the realm byte from the first hop to determine the number of hops to read, the first hop still has room for 64 bytes of payload (per-hop-payload + HMAC size - realm byte). Any intermediate hop has the full 65 bytes available. The last hop has 33 bytes of payload available, since we will use its HMAC to pass it on to the next node in the path. Notice that we do not decrypt the payloads after the first since processing the first hop already decrypted all the following hops, including the ones we'll be processing. In addition we get a better use of the limited space that we have available and the entire payload is contiguous in memory and can be passed out to the parser as is, without having to stitch it together.
The implementation is also rather trivial, all we need to do is to pass the payload as a byte slice out during processing, and to get the next onion instead of shifting by 65 bytes and padding with 0s, we shift by
65*nhops
and pad that with 0s. So the only thing we really need to do is to have therightShift
,headerWithPadding
, andcopy
not take 65, but 65*nhops as arguments.Closes #2689