Skip to content
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

Nonce simplification #4

Merged
merged 9 commits into from
Jan 25, 2023

Conversation

arik-so
Copy link

@arik-so arik-so commented Oct 20, 2022

No description provided.

@Roasbeef
Copy link
Owner

Hey can y'all remove that first commit from the branch? It ends up breaking the static remote key semantics, which are important to retain IMO. I've commented on that PR separately. Once that's gone I think we can have this land, and I'll do a few other corrections. I think this also might be based off a dated version of my original branch?

* [`66*byte`:`nonces`]
1. type: 4 (`musig2_nonce`)
2. data:
* [`66*byte`:`public_nonce`]
Copy link
Owner

Choose a reason for hiding this comment

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

I still think having some sort of qualifier here that adds more context to the nonces sent would be more useful.

For instance, this is Bob (responder of the channel), sending his local nonce which is used to allow Alice to sign the first commitment transaction. We can add more context by calling this either the remote nonce, or Bob's verification nonce.

Choose a reason for hiding this comment

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

Not opposed to doing so, but the idea was for it to be clear based on the TLV type/field alone, since local/verification nonces will always be sent on their own, and remote/signing nonces will always be sent along with the partial sig.

Copy link
Owner

Choose a reason for hiding this comment

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

Gotcha, then perhaps we can define the ints used for the types above in the preliminary section, for that to flow elsewhere?

Copy link
Author

Choose a reason for hiding this comment

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

You mean the TLV ID ints? Sure, we can, but for implementers that might also involve a lot of scrolling back and forth. I think I'm personally in favor of duplication, but I don't have a strong opinion.

Copy link

Choose a reason for hiding this comment

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

Agree on naming the nonce field according to what they actually are. Makes it much easier to just glance at the TLVs and know what the data is.

Copy link
Author

Choose a reason for hiding this comment

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

We ended up going with next_local_nonce for the standalone nonce's name.

bolt-simple-taproot.md Outdated Show resolved Hide resolved
- MUST set `verification_musig2_pubnonce` and `signing_musig2_pubnonce` to the
`musig2` public nonce as specified by the `NonceGen` algorithm of
`bip-musig2`
- MUST set `musig2_nonce` to the `musig2` public nonce used to sign local commitments as specified by the `NonceGen`
Copy link
Owner

Choose a reason for hiding this comment

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

I think this should read something like "public nonce the responder wants the initiator to use when signing their local commitment".

Choose a reason for hiding this comment

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

We could tack that on as an additional requirement perhaps? We should still make it clear that the nonce the sender is sending standalone is the their nonce when signing their commitments.

Copy link
Author

@arik-so arik-so Nov 15, 2022

Choose a reason for hiding this comment

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

Or, tacking on to @Roasbeef's comment, it could also stem from the name if it were something like next_musig_nonce or next_verification_nonce. Admittedly, I find "verification" to be a somewhat confusing term, so would personally prefer next_local_nonce, should we opt to go this route.

Copy link
Owner

Choose a reason for hiding this comment

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

sender is sending standalone is the their nonce when signing their commitments.

a.k.a their signing nonce? ;)

Copy link
Author

Choose a reason for hiding this comment

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

Wilmer has much stronger intuition than I do for the nonce naming. I keep thinking of remote nonces as the verification nonces because when you receive that partial signature, that's the nonce that you receive alongside it to verify that partial signature – but that's the opposite of the intended nomenclature lol

Copy link
Author

Choose a reason for hiding this comment

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

We ended up going with next_local_nonce.

* [`98*byte`: `partial_signature || public_nonce`]
3. type: 4 (`musig2_nonce`)
4. data:
* [`66*byte`: `public_nonce`]
Copy link
Owner

Choose a reason for hiding this comment

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

Similar comment here re context: w/o loading the entire protocol into ones head, it's unclear what public_nonce actually even means here.

Copy link
Owner

Choose a reason for hiding this comment

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

Continuing w/ the above, Alice is sending either her local nonce or verification nonce here.

I still think the verification+signing stuff works pretty well. With this new formulation in the PR:

  • signatures include one's signing nonce
  • some messages also send over the peer's verification nonce

Copy link
Owner

Choose a reason for hiding this comment

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

AFAICT, the main goal here is to send nonces only at the very last step. As a result, we end up sending 2 nonces here (Alice's signing nonce and her verification nonce).

Is that really better than having her send it at the very start in open_channel? Then at this point, all nonces are already known and only a signature needs to be sent.

IMO, the requirements w.r.t holding this in memory until the flow complete are more or less identical.

Copy link
Owner

Choose a reason for hiding this comment

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

Also re the partial signature thing: originally the idea was that if we have a field, then why not just use the whole thing. Hence the idea to attach something to the 32-byte partial signature to round things out.

If nonces are known ahead of time, then that can just have empty bytes at the end.

The other week you mentioned preferring not to have "contextual parsing", but that's already the case given that you need to know what features and the types of the TLV fields to parse this new message (compared to knowing that the sig will just be 32 bytes).

Copy link
Owner

Choose a reason for hiding this comment

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

Is that really better than having her send it at the very start in open_channel? Then at this point, all nonces are already known and only a signature needs to be sent.

Also today after accept_channel is sent, all the necessaray fundign params have been exchanged (channel config effectively static). With this, you continue to send funding params even up until the very last message in the funding flow.

Choose a reason for hiding this comment

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

One of the main reasons behind our proposal is that (in our opinion) it is much easier to reason about which nonce needs to be sent when and how should it be used. As @arik-so and I tried to discuss the how the protocol changes worked, we realized the local/remote and verification/signing terminology work well for one side but break down for the other, so we avoided them completely.

Also re the partial signature thing: originally the idea was that if we have a field, then why not just use the whole thing. Hence the idea to attach something to the 32-byte partial signature to round things out.
If nonces are known ahead of time, then that can just have empty bytes at the end.
The other week you mentioned preferring not to have "contextual parsing", but that's already the case given that you need to know what features and the types of the TLV fields to parse this new message (compared to knowing that the sig will just be 32 bytes).

I agree with you on the "contextual parsing" part, but the reason for the TLV is due to the simpler nonce interpretation. We were forced to use one since we're coupling the partial sig with a public nonce, so they wouldn't fit in the message's existing sig field. If we choose to forgo that, and commit everything upfront as previously done, then I'm not opposed to reusing the field for the partial sig and keeping the remaining 32 bytes empty.

Also today after accept_channel is sent, all the necessaray fundign params have been exchanged (channel config effectively static). With this, you continue to send funding params even up until the very last message in the funding flow.

I don't see that as a big deal, I would rather keep the simpler interpretation of nonces over that.

Copy link
Owner

Choose a reason for hiding this comment

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

I would rather keep the simpler interpretation of nonces over that

So I think this is the main thing I'm contesting here: is sending nonces later in the process actually simpler? Or is it just shifting things around w/ minimal gain?

As an example, in the new flow, Alice doesn't send her signing+verification nonce until the 3rd message is exchanged. But there's no reason why she can't just send that in open_channel since there're no hard dependancies on her generating the nonce (the new musig 1.0 mixes in the private key always she gets randomness augmentation by default).

I think the argument is stronger w/ the state machine changes though, since Bob doesn't need to cache Alice's "next" signing nonce, as it'll just be delivered to him w/ the next sig.

Copy link
Author

Choose a reason for hiding this comment

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

So like you did, I also wanna break it down into the two components involved: one is sending the remote nonce as part of the partial signature, and the other is sending the local nonce just in time.

I don't think we have a disagreement regarding the utility of including the remote nonce with the partial signature, so really the discussion is about whether the first local nonce should be included in the open_channel message, or in funding_created. From an implementation perspective, there is, as you said, not much of a difference, because either way, the sender needs to cache the nonce until they receive a partial signature to verify the correctness. But there is a difference in the semantics.

Specifically, I believe that the nonce/partial signature dance is essentially a challenge response mechanism. Alice signals to Bob that she's ready for a signature, and here's the nonce that signature should aggregate against. When should that nonce be sent? In my opinion, when readiness for that signature is signaled, i. e. in the message that immediately precedes the partial signature being sent by the remote party.

Separately, Wilmer made the point that the open_channel message is really about setting up the channel parameters, and the nonce for the first signature really isn't a parameter of the channel.

So these are the reasons that we think it makes semantically more sense for the local nonce to be moved to the message that we moved them to, but again, as you pointed out, there's no big difference from the standpoint of actually needing to implement it — just from understanding the rationale behind why the implementation needs to be a certain way.

Copy link

Choose a reason for hiding this comment

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

So I think this is the main thing I'm contesting here: is sending nonces later in the process actually simpler? Or is it just shifting things around w/ minimal gain?

It is definitely just shifting things around, but I think it has some merit. Not sending nonces upfront makes more logical sense in my head, as why would you send them before you know whether you'll ever get so far in the funding process that you'll actually need them?

Copy link
Author

Choose a reason for hiding this comment

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

Good point!

By the way, if we all agree that remote nonces should be made part of the partial signature, I think it would be somewhat less confusing to external readers if we were referring to the nonce in singular, given that it's really only the local nonce that has its own dedicated TLV field.

bolt-simple-taproot.md Outdated Show resolved Hide resolved
bolt-simple-taproot.md Outdated Show resolved Hide resolved
bolt-simple-taproot.md Outdated Show resolved Hide resolved
allows the receiver to generate an additional commitment.
1. type: 2 (`partial_signature_with_nonce`)
2. data:
* [`98*byte`: `partial_signature || public_nonce`]
Copy link
Owner

Choose a reason for hiding this comment

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

I think this change to the commitment dance is much more compelling that the initial funding changes.

Before it was:

  • both sides exchange signing+verification nonces at the start of the connection
  • when you sign, you send your next signing nonce (you'll use to sign state N+1)
  • when they revoke, they send their next verification nonce (you'll use to sign state N+1)

With this it's:

  • both sides exchange a verification/local nonce at the start
  • you sign sign, you send your signing nonce (lets them verify it)
  • when they revoke, they send their next verification nonce

Copy link
Owner

Choose a reason for hiding this comment

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

Typing it out then reading it back, doesn't seem as striking, since the revoke is more or less the same, and you're still sending a nonce w/ the sig, just that it's the next/current nonce instead.

Copy link
Owner

Choose a reason for hiding this comment

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

I think one nice part about sending the nonce along w/ the sig at the final step is: the message itself can be passed as aux input to the nonce generation function, binding the nonce to the commitment transaction itself (see prior point about being able to retransmit w/ diff HTLCs).

bolt-simple-taproot.md Show resolved Hide resolved
bolt-simple-taproot.md Show resolved Hide resolved
@Roasbeef
Copy link
Owner

Roasbeef commented Nov 15, 2022

So I did a proper pass over everything, and came away not really grasping why the JIT nonce is so great for the sender. They still need to write their local/verification nonce to disk in order to generate a signature, if the nonce isn't counter based.

I also think we should retain the local/verification nonce terminology somewhere, since it makes what's being sent self describing. We can just say signing_nonce (defined at the top) and be done w/ it, instead of saying listing musig2_nonce, then: "the sender must send the nonce they used to sign the commitment to the remote party.

I still think we should just prescribe that a counter be used, since it means that no new cryptographic material needs to be written to disk for this flavor of taproot channels. But ofc we can do this in another change, as ideally we add a distinct section re nonce assumptions and recommendations, etc.

@arik-so
Copy link
Author

arik-so commented Nov 16, 2022

btw, we can remove all the low-level recommendations and introduce verbiage that discriminates between calculation and cache retrieval of the combined funding key

bolt-simple-taproot.md Show resolved Hide resolved
bolt-simple-taproot.md Outdated Show resolved Hide resolved
* [`66*byte`:`nonces`]
1. type: 4 (`musig2_nonce`)
2. data:
* [`66*byte`:`public_nonce`]
Copy link

Choose a reason for hiding this comment

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

Agree on naming the nonce field according to what they actually are. Makes it much easier to just glance at the TLVs and know what the data is.

* [`98*byte`: `partial_signature || public_nonce`]
3. type: 4 (`musig2_nonce`)
4. data:
* [`66*byte`: `public_nonce`]
Copy link

Choose a reason for hiding this comment

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

So I think this is the main thing I'm contesting here: is sending nonces later in the process actually simpler? Or is it just shifting things around w/ minimal gain?

It is definitely just shifting things around, but I think it has some merit. Not sending nonces upfront makes more logical sense in my head, as why would you send them before you know whether you'll ever get so far in the funding process that you'll actually need them?

@Roasbeef Roasbeef merged commit ec894dd into Roasbeef:simple-taproot-chans Jan 25, 2023
Sign up for free to 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.

4 participants