-
Notifications
You must be signed in to change notification settings - Fork 914
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
Variable hop payload size for sphinx #2689
Variable hop payload size for sphinx #2689
Conversation
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.
just a few things that caught my eye
if (hop->realm == 0x00) | ||
return 65; | ||
|
||
if (size < 0xFD) |
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 comment would be helpful here
common/sphinx.c
Outdated
|
||
/* The hop must accomodate the hop_payload, as well as the varint | ||
* describing the length and HMAC. */ | ||
return vsize + size + 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.
extra semicolon
common/sphinx.c
Outdated
@@ -135,10 +137,14 @@ void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey, | |||
const struct short_channel_id *scid, | |||
struct amount_msat forward, u32 outgoing_cltv) | |||
{ | |||
u8 padding[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
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.
perhaps this could be const u8 padding[]
u8 *buf = tal_arr(path, u8, 0); | ||
towire_short_channel_id(&buf, scid); | ||
towire_u64(&buf, forward.millisatoshis); /* Raw: low-level serializer */ | ||
towire_u32(&buf, outgoing_cltv); | ||
towire(&buf, padding, ARRAY_SIZE(padding)); |
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.
is sizeof(padding)
instead of ARRAY_SIZE
the preferred way to do this with byte-sized elements? not sure if the codebase has a particular style preference here.
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.
ARRAY_SIZE is basically always preferable when you have an array. Unlike sizeof(), it will break compile if the argument is a pointer instead of an array, due to refactoring.
pos += varint_put(dest+pos, raw_size); | ||
|
||
memcpy(dest + pos, hop->payload, raw_size); | ||
pos += raw_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.
this is incremented but not used afterwards, is that intended?
common/sphinx.c
Outdated
|
||
memcpy(dest + pos, hop->payload, raw_size); | ||
pos += raw_size; | ||
memcpy(dest + hop_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.
it's hard to tell what's going on here, but perhaps there could be an assert(pos < dest + hop_size - HMAC_SIZE)
if I'm reading this correctly.
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.
Yes, I'm pretty sure this should be "memcpy(dest + pos, hop->hmac, HMAC_SIZE); pos += HMAC_SIZE; assert(pos == hop_size);"
ALso, I don't think we should be memset() dest to 0 at the top: I'd prefer to memset just the expected padding in the legacy path. That way valgrind will catch any bugs if we miss some bytes somehow.
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.
Made more explicit:
memcpy(dest + pos, hop->payload, raw_size);
pos += raw_size;
padding_size = hop_size - pos - HMAC_SIZE;
memset(dest + pos, 0, padding_size);
pos += padding_size;
memcpy(dest + pos, 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.
I still like to see an assert(pos == hop_size); at the end?
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.
Some good feedback from @jb55, and one nasty change from me re: endianness.
I don't think devtools/onion should do JSON, I think common/test/run-sphinx (or a new file) should.
common/sphinx.c
Outdated
|
||
memcpy(dest + pos, hop->payload, raw_size); | ||
pos += raw_size; | ||
memcpy(dest + hop_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.
Yes, I'm pretty sure this should be "memcpy(dest + pos, hop->hmac, HMAC_SIZE); pos += HMAC_SIZE; assert(pos == hop_size);"
ALso, I don't think we should be memset() dest to 0 at the top: I'd prefer to memset just the expected padding in the legacy path. That way valgrind will catch any bugs if we miss some bytes somehow.
/* In addition to the raw payload we need to also shift the | ||
* length encoding itself and the HMAC away. */ | ||
vsize = varint_get(paddedheader, 3, &shift_size); | ||
shift_size += vsize + 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.
This is wrong on several levels.
- I worry what happens if we don't have 3 bytes here, can we be tricked into reading off the end?
- This uses bitcoin's varint and we are now using BigSize which is big-endian.
- shift_size needs to be sanity checked carefully!
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.
- I worry what happens if we don't have 3 bytes here, can we be tricked into reading off the end?
No, we have paddedheader[2*ROUTING_INFO_SIZE];
, i.e., 2600 bytes here.
- This uses bitcoin's varint and we are now using BigSize which is big-endian.
Yep, fixing up
- shift_size needs to be sanity checked carefully!
I went and just abort when shift_size >= ROUTING_INFO_SIZE
since that'd shift out all non-filler infos and leave us with garbage at the end anyway :-)
lightningd/peer_htlcs.c
Outdated
handle_localpay(hin, hin->cltv_expiry, &hin->payment_hash, | ||
hop_data->amt_forward, | ||
hop_data->outgoing_cltv); | ||
/* if (rs->nextcase == ONION_FORWARD) { | ||
struct gossip_resolve *gr = |
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.
Erk, please delete don't comment out.
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.
My bad, must've slipped in
@@ -1,10 +1,16 @@ | |||
#include <ccan/mem/mem.h> |
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.
Erk, I just repurposed onion.c as a generic onion builder, which will clash horribly with this.
My approach with the TLV tests (which are also JSON) was to quote the spec in the test case (so if it's wrong or changes, check-source points it out) then pulls the JSON out of itself. We should generalize that, IMHO.
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.
Yep, ran into quite a bit of trouble rebasing, but it's working fine now :-)
Rebased and fixed up to reflect feedback 😉 |
fixup ACK on my comments! |
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.
Minor changes only, I'm eager to apply this!
common/sphinx.c
Outdated
|
||
memcpy(dest + pos, hop->payload, raw_size); | ||
pos += raw_size; | ||
memcpy(dest + hop_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.
I still like to see an assert(pos == hop_size); at the end?
size_t bigsize_put(u8 buf[VARINT_MAX_LEN], varint_t v); | ||
|
||
/* Big-endian variant of varint_get, used in lightning */ | ||
size_t bigsize_get(const u8 *p, size_t max, varint_t *val); |
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.
These routines don't belong under bitcoin/, and since the last merge they're now redundant: we now have fromwire_bigsize()
and towire_bigsize()
.
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.
Agreed that they don't belong here, but they are useful since fromwire_*
/ towire_*
require tal_arr
which they can extend, whereas the sphinx operations require in-place operations. I tried using the fromwire
and towire
variants, but it turned out to involve way more work than just implementing it again.
I can move the functions in a later cleanup PR.
common/sphinx.c
Outdated
if (hop->realm != 0x00) | ||
return false; | ||
#endif | ||
|
||
memset(dest, 0, hop_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.
Still need to delete this...
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.
It get's remove in a later commit :-)
devtools/onion.c
Outdated
if (secp256k1_ec_pubkey_create(secp256k1_ctx, &path[i].pubkey, | ||
privkeys[i]) != 1) | ||
errx(1, "Could not decode pubkey"); | ||
assert(klen == 2 * PUBKEY_LEN || klen == 2 * PRIVKEY_LEN); |
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.
Remove this assert, since you give a proper message below?
errx(1, "Could not decode pubkey"); | ||
assert(klen == 2 * PUBKEY_LEN || klen == 2 * PRIVKEY_LEN); | ||
if (klen == 2 * PRIVKEY_LEN) { | ||
if (!hex_decode(argv[1 + i], klen, rawprivkey, PRIVKEY_LEN)) |
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.
I would combine these two, for simplicity. hex_decode() will fail if the wrong length, and it avoids magic 2 *
. The other option would be if (hex_data_size(klen) == PRIVKEY_LEN)
but it still seems redundant?
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.
Switched to use hex_data_size
and pubkey_from_hexstr
to do the conversion in one go. Would a counterpart for privkeys be desirable as well (privkey_from_hexstr
)?
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.
Hmm, hex_decode does this test internally, so it's redundant. But not harmful, and I really want this merged :)
devtools/onion.c
Outdated
fprintf(stderr, | ||
"Provided key is neither a pubkey nor a " | ||
"privkey: %s\n", | ||
argv[1 + 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.
errx()
devtools/onion.c
Outdated
@@ -12,32 +13,55 @@ | |||
#include <unistd.h> | |||
|
|||
#define ASSOC_DATA_SIZE 32 | |||
#define PUBKEY_LEN 33 | |||
#define PRIVKEY_LEN 32 |
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.
PUBKEY_CMPR_LEN
from bitcoin/pubkey.h? Hmm we don't have a convenient PRIVKEY_LEN though...
If you're bored you could add one to bitcoin/privkey.h :)
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>
Clean rebase to get code back on top of |
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 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>
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>
The realm has lost significance, so let's unify this into the type. Signed-off-by: Christian Decker <decker.christian@gmail.com>
See lightning/bolts#619 and lightning/bolts#619 for discussion. Signed-off-by: Christian Decker <decker.christian@gmail.com>
Simplifying some operations, erroring in some cases and moving to global defines for constants. Suggested-by: Rusty Russell <@rustyrussell> Signed-off-by: Christian Decker <decker.christian@gmail.com>
@rustyrussell I added the last commit to address your feedback. I think the last open question is the use of |
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.
Ack ddf7da1
errx(1, "Could not decode pubkey"); | ||
assert(klen == 2 * PUBKEY_LEN || klen == 2 * PRIVKEY_LEN); | ||
if (klen == 2 * PRIVKEY_LEN) { | ||
if (!hex_decode(argv[1 + i], klen, rawprivkey, PRIVKEY_LEN)) |
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.
Hmm, hex_decode does this test internally, so it's redundant. But not harmful, and I really want this merged :)
This is the competing proposal to #2363. It implements the variable size
hop_payloads
discussed duriing the last IRC spec meeting.It is implemented as fixups based on that PR, so you should be able review just the
fixup!
commits and get a good picture of the differences. I also cleaned up all the common mentions of frames in the final commit, but left it in other places to facilitate reviews.Closes #2363