-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
[6/?] - lnwallet+chancloser: update channel state machine and co-op close for musig2 flow #7345
[6/?] - lnwallet+chancloser: update channel state machine and co-op close for musig2 flow #7345
Conversation
@@ -4462,11 +4462,30 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, | |||
return nil, err | |||
} | |||
|
|||
htlcAmt := int64(htlc.Amount.ToSatoshis()) | |||
|
|||
if chanType.IsTaproot() { |
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 wonder if it's possible to only have this taproot check in a single place, quite challenging i guess. atm it's scattered in all the "components", like CreateHtlcSuccessTx
also does this check.
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.
Yeah it's a bit scattered right now. In other PR @joostjager brought up the idea we initially had to sort of split things out into more of a set of "builder" implementations of a new interface. The idea then is that we'd init the commitment/HTLC builder once, then call into that. That way we can remove all the branching we have for taproot vs not. It's a bigger change/refactor though, so happy to pursue it, but I think makes sense to defer for another PR series.
The other upcoming change we have is the taproot assets set of changes, which'll add yet another channel type, which brings its own unique attriobutes along. I think this would be a good time to make such a change.
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.
high level first pass comments 🤓
9636891
to
717c9a3
Compare
58e6bef
to
ce6879c
Compare
717c9a3
to
4a43a07
Compare
781a70f
to
2d4df80
Compare
4a43a07
to
2882db4
Compare
b9e8011
to
ee8526b
Compare
ee8526b
to
763d774
Compare
e16b892
to
d51ad03
Compare
Pushed up a new version, added tests for co-op close, and also tests for channel re-establish, PTAL! |
b3a68b0
to
07d8f36
Compare
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.
Looking gooooood! Mostly non-blocking questions/comments. One blocking one in the co-op close commit.
EDIT: And then ofc the RBF change is still required.
lnwallet/transactions.go
Outdated
if chanType.IsTaproot() { | ||
taprootOutputKey, err := input.TaprootSecondLevelHtlcScript( | ||
revocationKey, delayKey, csvDelay, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pkScript, err = input.PayToTaprootScript(taprootOutputKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
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.
no longer need this since the switch now happens in SecondLevelHtlcScript
:)
if err != nil { | ||
return nil, err | ||
} | ||
|
||
default: | ||
witnessScript, err = input.SecondLevelHtlcScript( |
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.
missing error check for this
@@ -3299,7 +3299,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, | |||
|
|||
// If the HTLC isn't dust, then we'll create an empty sign job |
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.
nit: commit message is a bit stale I think. The We also modify the sigpool to now use a input.Signature everywhere
part was in 5e7a97c
@@ -1319,12 +1319,16 @@ type LightningChannel struct { | |||
// log is a channel-specific logging instance. | |||
log btclog.Logger | |||
|
|||
// taprootNonceProducer is used to generate a shachain tree for the |
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.
nit: commit message a bit stale. This commit updates all the signature validation methods for the channel & not only the HTLC validation
case isTaproot && isInitiator && !feeMatchesOffer: | ||
return nil, false, fmt.Errorf("fee rate for "+ | ||
"taproot channels was not accepted: "+ | ||
"sent %v, got %v", | ||
c.idealFeeSat, remoteProposedFee) |
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.
should we send some kind of warning to the peer 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.
also - related to earlier question - should we make sure to re-set our closing verification nonce here? to ensure when we try close again that we dont use same nonce (since MusigChanCloser struct is still the same I think. I dont think it gets refreshed)
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.
Yeah we should actually send it back here: https://github.com/lightningnetwork/lnd/blob/master/peer/brontide.go#L3528-L3540
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.
What's try again mean here? When we fail above, the chancloser
is removed.
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.
When we fail above, the chancloser is removed.
Indeed - missed this. Mah bad
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.
Reviewed this PR to get a better understanding of the following PRs, think we need to fix CI tho.
// another musig2 session. In order to permit our implementation to not have to | ||
// write any secret nonce state to disk, we'll use the _next_ shachain | ||
// pre-image as our primary randomness source. When used to generate the nonce | ||
// again to broadcast our commitment hte current height will be used. |
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.
// again to broadcast our commitment hte current height will be used. | |
// again to broadcast our commitment the current height will be used. |
// We'll compare the proposed total fee, to what we've proposed during | ||
// the negotiations. If it doesn't match any of our prior offers, then | ||
// we'll attempt to ratchet the fee closer to | ||
// If this is a taproot channel, then it MUST have a partial |
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.
Something to do in the future, a general pattern we can have is case msg: processMsg or handleMsg
, eg, here we'd have processCloseFeeNegotition
to avoid the ever-growing long method and it's also easier to reason about it and test.
// NewCommitState wraps the various signatures needed to properly | ||
// propose/accept a new commitment state. This includes the signer's nonce for | ||
// musig2 channels. | ||
type NewCommitState struct { |
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 NextCommitState
? New
sounds like you are initializing a struct here.
|
||
// If we use the original offer, then Alice should accept this message, | ||
// and finalize the shutdown process. We expect a message here as Alice | ||
// will echo back the final message. |
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.
the spec doesn't explicitly say that we're allowed to echo back the final message -- this came up when we were testing zero-conf with LDK. I don't think we should change this here, but figured I'd point out
We need to export the enum as it'll now be used in areas such as the chan closer.
…to accept In this commit, we add a new NewCommitState struct. This preps us for the future change wherein a partial signature is also added to the mix. All related tests and type signatures have also been updated accordingly.
…ation In this commit, we update the channel state machine with a new set of functional options that can be used to create/set the musig session state. When a channel is made during the funding process, the set of nonces we want to use is already known, so we allow them to be passed in. Similarly, once the channel is confirmed, then we'll need to create another channel instance that this times carries the newly generated nonces to send along side funding_locked. We also add some utility methods to permit callers to properly generate nonces in the various contexts.
In this commit, we update the genRemoteHtlcSigJobs function to be able to generate taproot jobs. We also modify the sigpool to now use a input.Signature everywhere. This'll allow us to pass around both ECDSA and Schnorr signatures via the same interface. We use a tapscript sighash in this case, as all the HTLC spends will actually be script path spends.
In this commit, we update the genHtlcSigValidationJobs function to be taproot aware. As we actually need a schnorr signature for the taproot validation, we need to coerce the entire wire type into a schnorr sig with the ForceSchnorr() method.
In this commit, we update the co-op close flow to support the new musig2 keyspend flow. We'll use some new functional options to allow a caller to pass in an active musig2 session. If this is present, then we'll use that to complete the musig2 flow by signing with a partial signature, and then ultimately combining the signatures at the end.
In this commit, we fix a bug in the `deriveMusig2Shachain` function where it didn't actually use the passed in revocation root as part of the hmac invocation. We also modify the function to be more generally useable as well, as now the caller can just pass in the revocation root things should be derived from.
In this commit, we update the ChanSyncMsg to populate nonce information. With this change, we can now hide nonce generation further down in the pipeline and ensure that all callers will have the expected fields populated.
Before this commit, we would conditionally generate nonces in RevokeCurrentCommitment. We move this to generateRevocation as this is called when doing channel sync, and we want to make sure we send the correct set of nonces.
In this commit, we update the logic to handle nonce init in ProcessChanSyncMsg. Once a channel is already open, this is where we'll get the new nonce data from the remote party we'll use to gain the nonce we need to sign for their next state.
07d8f36
to
5c09d61
Compare
d4b6877
to
7764f65
Compare
Change Description
In this PR, we update the channel state machine to be aware of the new musig2 flow. Along the way, we update some util functions to ensure we can use them to generate scripts/transactions for both segwit v0 and v1 channels.
In this new flow, each time we send a signature, we sig a partial sig along with the nonce that we used to sign. Upon receiving that signature, the remote party will use the nonce to complete their local session (for that state).
Each time a signature is received, a party will generate another nonce that'll be used to create their next local state. This next local/verification nonce is then sent alongside the revoke_and_ack message. Upon receiving the revocation, the party that originally sent the sig, can then use the new local nonce to prep the session for the next state.
We also update the co-op close state machine to be proper taproot aware. This involves setting the new nonce and partial signatures fields in the shutdown and closing signed messages. The other big change here is that we'll short circuit most of the co-op close flow. Rather than attempt a "negotiation", we'll just have the responder accept w/e the initiator sends, as they're the ones that pay the fees in the end.
In the next PR, we'll hook the new reservation flow up to the funding manager, and fill out the other functionality needed for the other sub-systems to be aware of the new channel type.
Steps to Test
Steps for reviewers to follow to test the change.
Pull Request Checklist
Testing
Code Style and Documentation
[skip ci]
in the commit message for small changes.📝 Please see our Contribution Guidelines for further guidance.