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

Musig2: add new MuSig2 RPC methods to signrpc #6361

Merged
merged 9 commits into from Apr 29, 2022

Conversation

guggero
Copy link
Collaborator

@guggero guggero commented Mar 23, 2022

Depends on btcsuite/btcd#1820.

Adds a new MuSig2 API for cooperatively producing Schnorr signatures that can be used either for Taproot internal keys or for OP_CHECKSIG signatures within a tap script tree.

@guggero guggero added this to In progress in v0.15.0-beta via automation Mar 23, 2022
@guggero guggero added this to the v0.15.0 milestone Mar 23, 2022
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

The final component!

Completed an early initial pass focusing mainly on the protocol and client flow, which matches up pretty closely with what I had in mind.

lnrpc/signrpc/signer.proto Outdated Show resolved Hide resolved
Indicates whether all nonces required to start the signing process are known
now.
*/
bool have_all_nonces = 4;
Copy link
Member

Choose a reason for hiding this comment

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

Won't this always be false the first time a session is created?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can send a set of already known nonces. So for the last signer, we might already have all other nonces and therefore it can be true.

of 66 bytes. Can be split into the two 33-byte points to get the individual
nonces.
*/
bytes local_public_nonces = 3;
Copy link
Member

Choose a reason for hiding this comment

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

FWIW this is only actually needed for incremental verification, but maybe it's also useful on the caller side of things?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need to communicate the nonce to the other party, so we need to know it at some point, don't we? Or how would the nonce exchange work?

// MuSig2SessionIDSize is the size of a MuSig2 session ID. It is the
// length of the combined public key and the two public nonces, all of
// them being compressed points.
MuSig2SessionIDSize = 99
Copy link
Member

Choose a reason for hiding this comment

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

Ah I see this sort of combines the notions of the session+context into one. If we wanted to get away with a smaller value there, then we can just hash these items and use a single 32 byte value here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea with the hashing, added that.

@Roasbeef
Copy link
Member

What needs to be done to take this out of the draft stage? Merging of the upstream btcd PR?

@guggero
Copy link
Collaborator Author

guggero commented Apr 13, 2022

What needs to be done to take this out of the draft stage? Merging of the upstream btcd PR?

Mostly adding the MuSig2 methods to the signer interface so they can also be forwarded to the remote signer. And cleaning up some user input validation TODOs. So not a big diff, working on it today.

@Roasbeef Roasbeef moved this from In progress to Review in progress in v0.15.0-beta Apr 13, 2022
@guggero guggero force-pushed the musig2 branch 2 times, most recently from 9ba253e to 892d343 Compare April 14, 2022 16:13
@guggero guggero marked this pull request as ready for review April 14, 2022 16:13
@guggero
Copy link
Collaborator Author

guggero commented Apr 14, 2022

Took the PR out of draft state. Everything works now, including remote signing!

While working on remote signing support for MuSig2 I noticed that the standalone Taproot functionality does not work in remote mode. Turns out we need #6268 to enable that.
I'm going to work on that in a separate PR, commented out the failing remote mode itests for now.

Copy link
Collaborator

@bhandras bhandras left a comment

Choose a reason for hiding this comment

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

The only thing remaining is to make the linter happy and fix the mobile build.

Other than those it's a super clean PR, really enjoyed reviewing! Awesome addition to LND 🚀 Great work @guggero 🥇

lntest/itest/lnd_taproot_test.go Outdated Show resolved Hide resolved
lnrpc/signrpc/signer.proto Outdated Show resolved Hide resolved
@lightninglabs-deploy
Copy link

@Roasbeef: review reminder

@guggero
Copy link
Collaborator Author

guggero commented Apr 25, 2022

I added a convenience RPC called MuSig2CombineKeys to make it possible to just get the combined key and Taproot public key without creating a session.

I also went ahead and added a short doc explaining the current MuSig2 implementation and examples.

@guggero guggero force-pushed the musig2 branch 5 times, most recently from e42fc02 to d254573 Compare April 27, 2022 20:20
@guggero
Copy link
Collaborator Author

guggero commented Apr 27, 2022

I updated to the latest version of btcsuite/btcd#1820 and added an itest for using a MuSig2 combined key in a tap leaf with a simple OP_CHECKSIG script.

I also found a small bug in the helper function TapscriptPartialReveal that is used for the fee calculation. A proof for a tree with just a single leaf can be empty, so the inclusion proof can be variable size.

@Roasbeef
Copy link
Member

The btcd PR has been merged, so this can be rebased!

@Roasbeef
Copy link
Member

A proof for a tree with just a single leaf can be empty, so the inclusion proof can be variable size.

Yes in this case, the verifier just takes the leaf, computes one level of tapLeaf hashing and uses that directly. You can see this interaction here if you pretend the loop below doesn't actually exist: https://github.com/btcsuite/btcd/blob/master/txscript/taproot.go#L155-L177

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 🌂

require (
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/siphash v1.0.1 // indirect
Copy link
Member

Choose a reason for hiding this comment

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

None of this goes away with a go mod tidy run w/ Go 1.18?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, unfortunately not. Go 1.17/1.18 just puts all the indirect dependencies in their own block.

input/musig2.go Outdated

// MuSig2SessionID is a type for a session ID that is just a hash of the MuSig2
// combined key and the local public nonces.
type MuSig2SessionID = [sha256.Size]byte
Copy link
Member

Choose a reason for hiding this comment

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

FWIW this is actually a type alias (the =) sign, but not sure it actually needs to be?

Copy link
Member

Choose a reason for hiding this comment

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

Re the derivation of this: doesn't need to be accounted for yet, but w/ the new mode that lets you generate the nonce before the rest of the keys are known, we may just need to have this be derived using just our local key and the nonce info.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, good point. I thought the alias would make it easier to convert between the type and the hash returned by the sha256 package. But I don't think that's really required.

About your second comment: I'll address that in a follow-up PR. Would like to get this PR in as is to unblock some work down the dependency tree.

// already known, they can be submitted as well to reduce the number of
// method calls necessary later on.
MuSig2CreateSession(keychain.KeyLocator, []*btcec.PublicKey,
*MuSig2Tweaks, [][musig2.PubNonceSize]byte) (*MuSig2SessionInfo,
Copy link
Member

Choose a reason for hiding this comment

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

Hmm so we don't make the distinction between the context vs the session here? Is the param of the set of nonce optional? Looking at just this function signature, I would think no...

Copy link
Member

Choose a reason for hiding this comment

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

Looking at it again, maybe unifying things just makes the API simpler: users call into this once they have all the pubkey and nonce info.

Or is the passed nonce here actually our local nonce? Need to get further in the diff.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, we don't make the distinction because with the previous design of the API we needed the session to get our local nonce so we could give it to the other parties.

The list of other party public nonces is not optional as in dynamic argument list. But you can pass in nil if you're the first to create a session and don't know any nonces of other participants yet.

I'll see how this looks once I've incorporated the recent API changes in the context/session. I hope I can add that functionality with minimal changes to the RPC API surface.

lntest/itest/lnd_remote_signer_test.go Show resolved Hide resolved
lntest/itest/lnd_taproot_test.go Show resolved Hide resolved
docs/musig2.md Show resolved Hide resolved
@Roasbeef
Copy link
Member

With this commit we add the high-level MuSig2 signing methods to the
btcwallet which will later be exposed through an RPC interface.
The inclusion proof field in the TapscriptPartialReveal function was
incorrect. An inclusion proof can be zero or more elements of 32-byte
slices. So an empty inclusion proof can be valid too for a tree that
only consists of a single leaf.
@guggero guggero merged commit 9bbee09 into lightningnetwork:master Apr 29, 2022
v0.15.0-beta automation moved this from Reviewer approved + active testing to Done Apr 29, 2022
@guggero guggero deleted the musig2 branch April 29, 2022 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

4 participants