Variable hop payload size for sphinx#2689
Variable hop payload size for sphinx#2689rustyrussell merged 13 commits intoElementsProject:masterfrom
Conversation
jb55
left a comment
There was a problem hiding this comment.
just a few things that caught my eye
| if (hop->realm == 0x00) | ||
| return 65; | ||
|
|
||
| if (size < 0xFD) |
There was a problem hiding this comment.
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;; |
common/sphinx.c
Outdated
| 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.
perhaps this could be const u8 padding[]
| 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.
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.
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.
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.
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.
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.
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.
I still like to see an assert(pos == hop_size); at the end?
rustyrussell
left a comment
There was a problem hiding this comment.
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.
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.
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.
- 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
| hop_data->amt_forward, | ||
| hop_data->outgoing_cltv); | ||
| /* if (rs->nextcase == ONION_FORWARD) { | ||
| struct gossip_resolve *gr = |
There was a problem hiding this comment.
Erk, please delete don't comment out.
There was a problem hiding this comment.
My bad, must've slipped in
| @@ -1,10 +1,16 @@ | |||
| #include <ccan/mem/mem.h> | |||
There was a problem hiding this comment.
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.
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! |
rustyrussell
left a comment
There was a problem hiding this comment.
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.
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.
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.
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
| return false; | ||
| #endif | ||
|
|
||
| memset(dest, 0, hop_size); |
There was a problem hiding this comment.
Still need to delete this...
There was a problem hiding this comment.
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.
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.
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.
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.
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]); |
devtools/onion.c
Outdated
|
|
||
| #define ASSOC_DATA_SIZE 32 | ||
| #define PUBKEY_LEN 33 | ||
| #define PRIVKEY_LEN 32 |
There was a problem hiding this comment.
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 |
| 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.
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_payloadsdiscussed 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