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

multi: add ability to specify local nonces for musig2 signer rpc, add itest for remote signer taproot chans #7994

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/release-notes/release-notes-0.17.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ None
* Add [`--unused`](https://github.com/lightningnetwork/lnd/pull/6387) to
`lncli newaddr` command.

* [The `MuSig2SessionRequest` proto message now contains a field to allow a
caller to specify a custom signing
nonce](https://github.com/lightningnetwork/lnd/pull/7994). This can be useful
for protocol where an external nonces must be pre-generated before the full
session can be completed.


## Code Health
* Updated [our fork for serializing protobuf as JSON to be based on the
latest version of `google.golang.org/protobuf` instead of the deprecated
Expand Down
26 changes: 17 additions & 9 deletions input/musig2.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ type MuSig2Signer interface {
// already known, they can be submitted as well to reduce the number of
// method calls necessary later on.
//
// The set of sessionOpts are _optional_ and allow a caller to modify
// the generated sessions. As an example the local nonce might already
// be generated ahead of time.
// The localNonces field is optional. If it is set, then the specified
// nonces will be used instead of generating from scratch. This is
// useful in instances where the nonces are generated ahead of time
// before the set of signers is known.
MuSig2CreateSession(MuSig2Version, keychain.KeyLocator,
[]*btcec.PublicKey, *MuSig2Tweaks, [][musig2.PubNonceSize]byte,
...musig2.SessionOption) (*MuSig2SessionInfo, error)
*musig2.Nonces) (*MuSig2SessionInfo, error)

// MuSig2RegisterNonces registers one or more public nonces of other
// signing participants for a session identified by its ID. This method
Expand Down Expand Up @@ -379,18 +380,18 @@ func combineKeysV040(allSignerPubKeys []*btcec.PublicKey, sortKeys bool,
// MuSig2CreateContext creates a new MuSig2 signing context.
func MuSig2CreateContext(bipVersion MuSig2Version, privKey *btcec.PrivateKey,
allSignerPubKeys []*btcec.PublicKey, tweaks *MuSig2Tweaks,
sessionOpts ...musig2.SessionOption,
localNonces *musig2.Nonces,
) (MuSig2Context, MuSig2Session, error) {

switch bipVersion {
case MuSig2Version040:
return createContextV040(
privKey, allSignerPubKeys, tweaks, sessionOpts...,
privKey, allSignerPubKeys, tweaks, localNonces,
)

case MuSig2Version100RC2:
return createContextV100RC2(
privKey, allSignerPubKeys, tweaks, sessionOpts...,
privKey, allSignerPubKeys, tweaks, localNonces,
)

default:
Expand All @@ -403,7 +404,7 @@ func MuSig2CreateContext(bipVersion MuSig2Version, privKey *btcec.PrivateKey,
// BIP draft version 1.0.0rc2.
func createContextV100RC2(privKey *btcec.PrivateKey,
allSignerPubKeys []*btcec.PublicKey, tweaks *MuSig2Tweaks,
sessionOpts ...musig2.SessionOption,
localNonces *musig2.Nonces,
) (*musig2.Context, *musig2.Session, error) {

// The context keeps track of all signing keys and our local key.
Expand All @@ -419,6 +420,13 @@ func createContextV100RC2(privKey *btcec.PrivateKey,
"context: %v", err)
}

var sessionOpts []musig2.SessionOption
if localNonces != nil {
sessionOpts = append(
sessionOpts, musig2.WithPreGeneratedNonce(localNonces),
)
}

muSigSession, err := muSigContext.NewSession(sessionOpts...)
if err != nil {
return nil, nil, fmt.Errorf("error creating MuSig2 signing "+
Expand All @@ -432,7 +440,7 @@ func createContextV100RC2(privKey *btcec.PrivateKey,
// draft version 0.4.0.
func createContextV040(privKey *btcec.PrivateKey,
allSignerPubKeys []*btcec.PublicKey, tweaks *MuSig2Tweaks,
_ ...musig2.SessionOption,
_ *musig2.Nonces,
) (*musig2v040.Context, *musig2v040.Session, error) {

// The context keeps track of all signing keys and our local key.
Expand Down
4 changes: 2 additions & 2 deletions input/musig2_session_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func NewMusigSessionManager(keyFetcher PrivKeyFetcher) *MusigSessionManager {
func (m *MusigSessionManager) MuSig2CreateSession(bipVersion MuSig2Version,
keyLoc keychain.KeyLocator, allSignerPubKeys []*btcec.PublicKey,
tweaks *MuSig2Tweaks, otherSignerNonces [][musig2.PubNonceSize]byte,
sessionOpts ...musig2.SessionOption) (*MuSig2SessionInfo, error) {
localNonces *musig2.Nonces) (*MuSig2SessionInfo, error) {

// We need to derive the private key for signing. In the remote signing
// setup, this whole RPC call will be forwarded to the signing
Expand All @@ -78,7 +78,7 @@ func (m *MusigSessionManager) MuSig2CreateSession(bipVersion MuSig2Version,
// Create a signing context and session with the given private key and
// list of all known signer public keys.
musigContext, musigSession, err := MuSig2CreateContext(
bipVersion, privKey, allSignerPubKeys, tweaks, sessionOpts...,
bipVersion, privKey, allSignerPubKeys, tweaks, localNonces,
)
if err != nil {
return nil, fmt.Errorf("error creating signing context: %w",
Expand Down
23 changes: 19 additions & 4 deletions itest/lnd_payment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,34 @@ func testAsyncPayments(ht *lntest.HarnessTest) {
ht.EnsureConnected(alice, bob)
ht.FundCoins(btcutil.SatoshiPerBitcoin, alice)

runAsyncPayments(ht, alice, bob)
runAsyncPayments(ht, alice, bob, nil)
}

// runAsyncPayments tests the performance of the async payments.
func runAsyncPayments(ht *lntest.HarnessTest, alice, bob *node.HarnessNode) {
func runAsyncPayments(ht *lntest.HarnessTest, alice, bob *node.HarnessNode,
commitType *lnrpc.CommitmentType) {

const paymentAmt = 100

channelCapacity := btcutil.Amount(paymentAmt * 2000)

chanArgs := lntest.OpenChannelParams{
Amt: channelCapacity,
}

if commitType != nil {
chanArgs.CommitmentType = *commitType

if *commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT {
chanArgs.Private = true
}
}

// First establish a channel with a capacity equals to the overall
// amount of payments, between Alice and Bob, at the end of the test
// Alice should send all money from her side to Bob.
channelCapacity := btcutil.Amount(paymentAmt * 2000)
chanPoint := ht.OpenChannel(
alice, bob, lntest.OpenChannelParams{Amt: channelCapacity},
alice, bob, chanArgs,
)

info := ht.QueryChannelByChanPoint(alice, chanPoint)
Expand Down
28 changes: 24 additions & 4 deletions itest/lnd_remote_signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
name string
randomSeed bool
sendCoins bool
commitType lnrpc.CommitmentType
fn func(tt *lntest.HarnessTest,
wo, carol *node.HarnessNode)
}
Expand Down Expand Up @@ -94,8 +95,19 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
name: "async payments",
sendCoins: true,
fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) {
runAsyncPayments(tt, wo, carol)
runAsyncPayments(tt, wo, carol, nil)
},
}, {
name: "async payments taproot",
sendCoins: true,
fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) {
commitType := lnrpc.CommitmentType_SIMPLE_TAPROOT

runAsyncPayments(
tt, wo, carol, &commitType,
)
},
commitType: lnrpc.CommitmentType_SIMPLE_TAPROOT,
}, {
name: "shared key",
fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) {
Expand Down Expand Up @@ -199,11 +211,18 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
require.NoError(st, err)
}

var commitArgs []string
if subTest.commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT {
commitArgs = lntest.NodeArgsForCommitType(
subTest.commitType,
)
}

// WatchOnly is the node that has a watch-only wallet and uses
// the Signer node for any operation that requires access to
// private keys.
watchOnly := st.NewNodeRemoteSigner(
"WatchOnly", []string{
"WatchOnly", append([]string{
"--remotesigner.enable",
fmt.Sprintf(
"--remotesigner.rpchost=localhost:%d",
Expand All @@ -217,7 +236,8 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
"--remotesigner.macaroonpath=%s",
signer.Cfg.AdminMacPath,
),
}, password, &lnrpc.WatchOnly{
}, commitArgs...),
password, &lnrpc.WatchOnly{
MasterKeyBirthdayTimestamp: 0,
MasterKeyFingerprint: nil,
Accounts: watchOnlyAccounts,
Expand All @@ -235,7 +255,7 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
)
}

carol := st.NewNode("carol", nil)
carol := st.NewNode("carol", commitArgs)
st.EnsureConnected(watchOnly, carol)

return signer, watchOnly, carol
Expand Down