From 58d0707a77c4c535cb68260728a065eb982ef716 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 27 Jan 2023 16:13:20 +0100 Subject: [PATCH] itest: test both MuSig2 versions --- lntemp/rpc/signer.go | 28 +++++ lntest/itest/lnd_remote_signer_test.go | 20 ++- lntest/itest/lnd_taproot_test.go | 166 +++++++++++++++++++------ 3 files changed, 171 insertions(+), 43 deletions(-) diff --git a/lntemp/rpc/signer.go b/lntemp/rpc/signer.go index fea7bf9dd..79a680c2b 100644 --- a/lntemp/rpc/signer.go +++ b/lntemp/rpc/signer.go @@ -72,6 +72,20 @@ func (h *HarnessRPC) MuSig2CreateSession( return resp } +// MuSig2CreateSessionErr makes an RPC call to the node's SignerClient and +// asserts an error is returned. +func (h *HarnessRPC) MuSig2CreateSessionErr( + req *signrpc.MuSig2SessionRequest) error { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.Signer.MuSig2CreateSession(ctxt, req) + require.Error(h, err, "expected error from calling MuSig2CreateSession") + + return err +} + // MuSig2CombineKeys makes a RPC call to the node's SignerClient and asserts. // //nolint:lll @@ -87,6 +101,20 @@ func (h *HarnessRPC) MuSig2CombineKeys( return resp } +// MuSig2CombineKeysErr makes an RPC call to the node's SignerClient and +// asserts an error is returned. +func (h *HarnessRPC) MuSig2CombineKeysErr( + req *signrpc.MuSig2CombineKeysRequest) error { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.Signer.MuSig2CombineKeys(ctxt, req) + require.Error(h, err, "expected error from calling MuSig2CombineKeys") + + return err +} + // MuSig2RegisterNonces makes a RPC call to the node's SignerClient and asserts. // //nolint:lll diff --git a/lntest/itest/lnd_remote_signer_test.go b/lntest/itest/lnd_remote_signer_test.go index 032301340..eccf396b9 100644 --- a/lntest/itest/lnd_remote_signer_test.go +++ b/lntest/itest/lnd_remote_signer_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcwallet/waddrmgr" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntemp/node" @@ -132,10 +133,21 @@ func testRemoteSigner(ht *lntemp.HarnessTest) { testTaprootSignOutputRawScriptSpend(tt, wo) testTaprootSignOutputRawKeySpendBip86(tt, wo) testTaprootSignOutputRawKeySpendRootHash(tt, wo) - testTaprootMuSig2KeySpendRootHash(tt, wo) - testTaprootMuSig2ScriptSpend(tt, wo) - testTaprootMuSig2KeySpendBip86(tt, wo) - testTaprootMuSig2CombinedLeafKeySpend(tt, wo) + + muSig2Versions := []signrpc.MuSig2Version{ + signrpc.MuSig2Version_MUSIG2_VERSION_V040, + signrpc.MuSig2Version_MUSIG2_VERSION_V100RC2, + } + for _, version := range muSig2Versions { + testTaprootMuSig2KeySpendRootHash( + tt, wo, version, + ) + testTaprootMuSig2ScriptSpend(tt, wo, version) + testTaprootMuSig2KeySpendBip86(tt, wo, version) + testTaprootMuSig2CombinedLeafKeySpend( + tt, wo, version, + ) + } }, }} diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go index 17c619ab4..a04048195 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/lntest/itest/lnd_taproot_test.go @@ -60,11 +60,17 @@ func testTaproot(ht *lntemp.HarnessTest) { ) testTaprootSignOutputRawKeySpendRootHash(ht, ht.Alice) - testTaprootMuSig2KeySpendBip86(ht, ht.Alice) - testTaprootMuSig2KeySpendRootHash(ht, ht.Alice) - testTaprootMuSig2ScriptSpend(ht, ht.Alice) - testTaprootMuSig2CombinedLeafKeySpend(ht, ht.Alice) - testMuSig2CombineKey(ht, ht.Alice) + muSig2Versions := []signrpc.MuSig2Version{ + signrpc.MuSig2Version_MUSIG2_VERSION_V040, + signrpc.MuSig2Version_MUSIG2_VERSION_V100RC2, + } + for _, version := range muSig2Versions { + testTaprootMuSig2KeySpendBip86(ht, ht.Alice, version) + testTaprootMuSig2KeySpendRootHash(ht, ht.Alice, version) + testTaprootMuSig2ScriptSpend(ht, ht.Alice, version) + testTaprootMuSig2CombinedLeafKeySpend(ht, ht.Alice, version) + testMuSig2CombineKey(ht, ht.Alice, version) + } testTaprootImportTapscriptFullTree(ht, ht.Alice) testTaprootImportTapscriptPartialReveal(ht, ht.Alice) @@ -578,7 +584,7 @@ func testTaprootSignOutputRawKeySpendRootHash(ht *lntemp.HarnessTest, // testTaprootMuSig2KeySpendBip86 tests that a combined MuSig2 key can also be // used as a BIP-0086 key spend only key. func testTaprootMuSig2KeySpendBip86(ht *lntemp.HarnessTest, - alice *node.HarnessNode) { + alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're not going to commit to a script. So our taproot tweak will be // empty and just specify the necessary flag. @@ -586,10 +592,12 @@ func testTaprootMuSig2KeySpendBip86(ht *lntemp.HarnessTest, KeySpendOnly: true, } - keyDesc1, keyDesc2, keyDesc3, allPubKeys := deriveSigningKeys(ht, alice) + keyDesc1, keyDesc2, keyDesc3, allPubKeys := deriveSigningKeys( + ht, alice, version, + ) _, taprootKey, sessResp1, sessResp2, sessResp3 := createMuSigSessions( ht, alice, taprootTweak, keyDesc1, keyDesc2, keyDesc3, - allPubKeys, + allPubKeys, version, ) // Send some coins to the generated tapscript address. @@ -704,7 +712,7 @@ func testTaprootMuSig2KeySpendBip86(ht *lntemp.HarnessTest, // testTaprootMuSig2KeySpendRootHash tests that a tapscript address can also be // spent using a MuSig2 combined key. func testTaprootMuSig2KeySpendRootHash(ht *lntemp.HarnessTest, - alice *node.HarnessNode) { + alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're going to commit to a script as well. This is a hash lock with a // simple preimage of "foobar". We need to know this upfront so, we can @@ -717,11 +725,11 @@ func testTaprootMuSig2KeySpendRootHash(ht *lntemp.HarnessTest, } keyDesc1, keyDesc2, keyDesc3, allPubKeys := deriveSigningKeys( - ht, alice, + ht, alice, version, ) _, taprootKey, sessResp1, sessResp2, sessResp3 := createMuSigSessions( ht, alice, taprootTweak, keyDesc1, keyDesc2, keyDesc3, - allPubKeys, + allPubKeys, version, ) // Send some coins to the generated tapscript address. @@ -836,7 +844,7 @@ func testTaprootMuSig2KeySpendRootHash(ht *lntemp.HarnessTest, // testTaprootMuSig2ScriptSpend tests that a tapscript address with an internal // key that is a MuSig2 combined key can also be spent using the script path. func testTaprootMuSig2ScriptSpend(ht *lntemp.HarnessTest, - alice *node.HarnessNode) { + alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're going to commit to a script and spend the output using the // script. This is a hash lock with a simple preimage of "foobar". We @@ -849,11 +857,11 @@ func testTaprootMuSig2ScriptSpend(ht *lntemp.HarnessTest, } keyDesc1, keyDesc2, keyDesc3, allPubKeys := deriveSigningKeys( - ht, alice, + ht, alice, version, ) internalKey, taprootKey, _, _, _ := createMuSigSessions( ht, alice, taprootTweak, keyDesc1, keyDesc2, keyDesc3, - allPubKeys, + allPubKeys, version, ) // Because we know the internal key and the script we want to spend, we @@ -919,15 +927,16 @@ func testTaprootMuSig2ScriptSpend(ht *lntemp.HarnessTest, // testTaprootMuSig2CombinedLeafKeySpend tests that a MuSig2 combined key can be // used for an OP_CHECKSIG inside a tap script leaf spend. func testTaprootMuSig2CombinedLeafKeySpend(ht *lntemp.HarnessTest, - alice *node.HarnessNode) { + alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're using the combined MuSig2 key in a script leaf. So we need to // derive the combined key first, before we can build the script. keyDesc1, keyDesc2, keyDesc3, allPubKeys := deriveSigningKeys( - ht, alice, + ht, alice, version, ) req := &signrpc.MuSig2CombineKeysRequest{ AllSignerPubkeys: allPubKeys, + Version: version, } combineResp := alice.RPC.MuSig2CombineKeys(req) combinedPubKey, err := schnorr.ParsePubKey(combineResp.CombinedKey) @@ -982,6 +991,7 @@ func testTaprootMuSig2CombinedLeafKeySpend(ht *lntemp.HarnessTest, // Do the actual signing now. _, _, sessResp1, sessResp2, sessResp3 := createMuSigSessions( ht, alice, nil, keyDesc1, keyDesc2, keyDesc3, allPubKeys, + version, ) require.NoError(ht, err) @@ -1644,8 +1654,8 @@ func confirmAddress(ht *lntemp.HarnessTest, hn *node.HarnessNode, // deriveSigningKeys derives three signing keys and returns their descriptors, // as well as the public keys in the Schnorr serialized format. -func deriveSigningKeys(ht *lntemp.HarnessTest, - node *node.HarnessNode) (*signrpc.KeyDescriptor, +func deriveSigningKeys(ht *lntemp.HarnessTest, node *node.HarnessNode, + version signrpc.MuSig2Version) (*signrpc.KeyDescriptor, *signrpc.KeyDescriptor, *signrpc.KeyDescriptor, [][]byte) { // For muSig2 we need multiple keys. We derive three of them from the @@ -1666,10 +1676,21 @@ func deriveSigningKeys(ht *lntemp.HarnessTest, // Now that we have all three keys we can create three sessions, one // for each of the signers. This would of course normally not happen on // the same node. - allPubKeys := [][]byte{ - schnorr.SerializePubKey(pubKey1), - schnorr.SerializePubKey(pubKey2), - schnorr.SerializePubKey(pubKey3), + var allPubKeys [][]byte + switch version { + case signrpc.MuSig2Version_MUSIG2_VERSION_V040: + allPubKeys = [][]byte{ + schnorr.SerializePubKey(pubKey1), + schnorr.SerializePubKey(pubKey2), + schnorr.SerializePubKey(pubKey3), + } + + case signrpc.MuSig2Version_MUSIG2_VERSION_V100RC2: + allPubKeys = [][]byte{ + pubKey1.SerializeCompressed(), + pubKey2.SerializeCompressed(), + pubKey3.SerializeCompressed(), + } } return keyDesc1, keyDesc2, keyDesc3, allPubKeys @@ -1682,16 +1703,26 @@ func deriveSigningKeys(ht *lntemp.HarnessTest, func createMuSigSessions(ht *lntemp.HarnessTest, node *node.HarnessNode, taprootTweak *signrpc.TaprootTweakDesc, keyDesc1, keyDesc2, keyDesc3 *signrpc.KeyDescriptor, - allPubKeys [][]byte) (*btcec.PublicKey, *btcec.PublicKey, - *signrpc.MuSig2SessionResponse, *signrpc.MuSig2SessionResponse, - *signrpc.MuSig2SessionResponse) { + allPubKeys [][]byte, version signrpc.MuSig2Version) (*btcec.PublicKey, + *btcec.PublicKey, *signrpc.MuSig2SessionResponse, + *signrpc.MuSig2SessionResponse, *signrpc.MuSig2SessionResponse) { - req := &signrpc.MuSig2SessionRequest{ + // Make sure that when not specifying a version we get an error, since + // it is mandatory. + err := node.RPC.MuSig2CreateSessionErr(&signrpc.MuSig2SessionRequest{}) + require.ErrorContains(ht, err, "unknown MuSig2 version") + + // Create the actual session with the version specified. + sessResp1 := node.RPC.MuSig2CreateSession(&signrpc.MuSig2SessionRequest{ KeyLoc: keyDesc1.KeyLoc, AllSignerPubkeys: allPubKeys, TaprootTweak: taprootTweak, - } - sessResp1 := node.RPC.MuSig2CreateSession(req) + Version: version, + }) + require.Equal(ht, version, sessResp1.Version) + + // Make sure the version is returned correctly. + require.Equal(ht, version, sessResp1.Version) // Now that we have the three keys in a combined form, we want to make // sure the tweaking for the taproot key worked correctly. We first need @@ -1728,11 +1759,17 @@ func createMuSigSessions(ht *lntemp.HarnessTest, node *node.HarnessNode, ) } + // Same with the combine keys RPC, no version specified should give us + // an error. + err = node.RPC.MuSig2CombineKeysErr(&signrpc.MuSig2CombineKeysRequest{}) + require.ErrorContains(ht, err, "unknown MuSig2 version") + // We should also get the same keys when just calling the // MuSig2CombineKeys RPC. combineReq := &signrpc.MuSig2CombineKeysRequest{ AllSignerPubkeys: allPubKeys, TaprootTweak: taprootTweak, + Version: version, } combineResp := node.RPC.MuSig2CombineKeys(combineReq) require.Equal( @@ -1743,19 +1780,22 @@ func createMuSigSessions(ht *lntemp.HarnessTest, node *node.HarnessNode, ht, schnorr.SerializePubKey(internalKey), combineResp.TaprootInternalKey, ) + require.Equal(ht, version, combineResp.Version) // Everything is good so far, let's continue with creating the signing // session for the other two participants. - req = &signrpc.MuSig2SessionRequest{ + req := &signrpc.MuSig2SessionRequest{ KeyLoc: keyDesc2.KeyLoc, AllSignerPubkeys: allPubKeys, OtherSignerPublicNonces: [][]byte{ sessResp1.LocalPublicNonces, }, TaprootTweak: taprootTweak, + Version: version, } sessResp2 := node.RPC.MuSig2CreateSession(req) require.Equal(ht, sessResp1.CombinedKey, sessResp2.CombinedKey) + require.Equal(ht, version, sessResp2.Version) req = &signrpc.MuSig2SessionRequest{ KeyLoc: keyDesc3.KeyLoc, @@ -1765,10 +1805,11 @@ func createMuSigSessions(ht *lntemp.HarnessTest, node *node.HarnessNode, sessResp2.LocalPublicNonces, }, TaprootTweak: taprootTweak, + Version: version, } sessResp3 := node.RPC.MuSig2CreateSession(req) - require.NoError(ht, err) require.Equal(ht, sessResp2.CombinedKey, sessResp3.CombinedKey) + require.Equal(ht, version, sessResp3.Version) require.Equal(ht, true, sessResp3.HaveAllNonces) // We need to distribute the rest of the nonces. @@ -1863,7 +1904,9 @@ func testTaprootCoopClose(ht *lntemp.HarnessTest) { // testMuSig2CombineKey makes sure that combining a key with MuSig2 returns the // correct result according to the MuSig2 version specified. -func testMuSig2CombineKey(ht *lntemp.HarnessTest, alice *node.HarnessNode) { +func testMuSig2CombineKey(ht *lntemp.HarnessTest, alice *node.HarnessNode, + version signrpc.MuSig2Version) { + testVector040Key1 := hexDecode( "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE0" + "36F9", @@ -1877,24 +1920,69 @@ func testMuSig2CombineKey(ht *lntemp.HarnessTest, alice *node.HarnessNode) { "CA66", ) + testVector100Key1 := hexDecode( + "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BC" + + "E036F9", + ) + testVector100Key2 := hexDecode( + "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50" + + "2BA659", + ) + testVector100Key3 := hexDecode( + "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D0" + + "38CA66", + ) + + var allPubKeys [][]byte + switch version { + case signrpc.MuSig2Version_MUSIG2_VERSION_V040: + allPubKeys = [][]byte{ + testVector040Key1, testVector040Key2, testVector040Key3, + } + + case signrpc.MuSig2Version_MUSIG2_VERSION_V100RC2: + allPubKeys = [][]byte{ + testVector100Key1, testVector100Key2, testVector100Key3, + } + } + resp := alice.RPC.MuSig2CombineKeys(&signrpc.MuSig2CombineKeysRequest{ - AllSignerPubkeys: [][]byte{ - testVector040Key1, testVector040Key2, - testVector040Key3, - }, + AllSignerPubkeys: allPubKeys, TaprootTweak: &signrpc.TaprootTweakDesc{ KeySpendOnly: true, }, + Version: version, }) - expectedFinalKey := hexDecode( + expectedFinalKey040 := hexDecode( "5b257b4e785d61157ef5303051f45184bd5cb47bc4b4069ed4dd453645" + "9cb83b", ) - expectedPreTweakKey := hexDecode( + expectedPreTweakKey040 := hexDecode( "d70cd69a2647f7390973df48cbfa2ccc407b8b2d60b08c5f1641185c79" + "98a290", ) - require.Equal(ht, expectedFinalKey, resp.CombinedKey) - require.Equal(ht, expectedPreTweakKey, resp.TaprootInternalKey) + + expectedFinalKey100 := hexDecode( + "79e6c3e628c9bfbce91de6b7fb28e2aec7713d377cf260ab599dcbc40e54" + + "2312", + ) + expectedPreTweakKey100 := hexDecode( + "789d937bade6673538f3e28d8368dda4d0512f94da44cf477a505716d26a" + + "1575", + ) + + switch version { + case signrpc.MuSig2Version_MUSIG2_VERSION_V040: + require.Equal(ht, expectedFinalKey040, resp.CombinedKey) + require.Equal( + ht, expectedPreTweakKey040, resp.TaprootInternalKey, + ) + + case signrpc.MuSig2Version_MUSIG2_VERSION_V100RC2: + require.Equal(ht, expectedFinalKey100, resp.CombinedKey) + require.Equal( + ht, expectedPreTweakKey100, resp.TaprootInternalKey, + ) + } }