mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge 895b7624ac into 6d1dbcb599
This commit is contained in:
commit
89b034c4f4
4 changed files with 192 additions and 10 deletions
|
|
@ -1239,7 +1239,10 @@ func (s *loopInSwap) setState(state loopdb.SwapState) {
|
|||
}
|
||||
|
||||
// sharedSecretFromHash derives the shared secret from the swap hash using the
|
||||
// swap.KeyFamily family and zero as index.
|
||||
// swap.KeyFamily family and zero as index. The swap hash is first interpreted
|
||||
// with btcec.PrivKeyFromBytes semantics, so the derived ephemeral pubkey uses
|
||||
// the same modulo-N normalization as the internal-key code paths that consume
|
||||
// the resulting shared secret.
|
||||
func sharedSecretFromHash(ctx context.Context, signer lndclient.SignerClient,
|
||||
hash lntypes.Hash) ([32]byte, error) {
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
|
|
@ -28,9 +27,12 @@ func TestStaticAddressScript(t *testing.T) {
|
|||
clientPrivKey, clientPubKey := test.CreateKey(1)
|
||||
serverPrivKey, serverPubKey := test.CreateKey(2)
|
||||
|
||||
var clientKey, serverKey [32]byte
|
||||
copy(clientKey[:], clientPrivKey.Serialize())
|
||||
copy(serverKey[:], serverPrivKey.Serialize())
|
||||
|
||||
// Keys used for the Musig2 session.
|
||||
privKeys := []*btcec.PrivateKey{clientPrivKey, serverPrivKey}
|
||||
pubKeys := []*btcec.PublicKey{clientPubKey, serverPubKey}
|
||||
privKeys := [][32]byte{clientKey, serverKey}
|
||||
|
||||
// Create a new static address.
|
||||
staticAddress, err := NewStaticAddress(
|
||||
|
|
@ -91,7 +93,7 @@ func TestStaticAddressScript(t *testing.T) {
|
|||
}
|
||||
|
||||
sig, err := utils.MuSig2Sign(
|
||||
version, privKeys, pubKeys, tweak, msg,
|
||||
version, privKeys, tweak, msg,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,44 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
)
|
||||
|
||||
// MuSig2Sign will create a MuSig2 signature for the passed message using the
|
||||
// passed private keys.
|
||||
func MuSig2Sign(version input.MuSig2Version, privKeys []*btcec.PrivateKey,
|
||||
pubKeys []*btcec.PublicKey, tweaks *input.MuSig2Tweaks,
|
||||
msg [32]byte) ([]byte, error) {
|
||||
// passed raw private keys. Raw keys are interpreted with
|
||||
// btcec.PrivKeyFromBytes semantics, which normalize 32-byte inputs modulo the
|
||||
// secp256k1 group order instead of rejecting out-of-range values. It expects
|
||||
// at least two signing keys.
|
||||
func MuSig2Sign(version input.MuSig2Version, keys [][32]byte,
|
||||
tweaks *input.MuSig2Tweaks, msg [32]byte) ([]byte, error) {
|
||||
|
||||
privKeys := make([]*btcec.PrivateKey, len(keys))
|
||||
pubKeys := make([]*btcec.PublicKey, len(keys))
|
||||
|
||||
// First parse the raw private keys and also create the corresponding
|
||||
// public keys. This preserves the same normalization semantics used
|
||||
// when these raw keys are turned into pubkeys elsewhere in the protocol.
|
||||
for i, key := range keys {
|
||||
privKeys[i], pubKeys[i] = btcec.PrivKeyFromBytes(key[:])
|
||||
|
||||
// MuSig2 v0.4 expects x-only public keys.
|
||||
if version == input.MuSig2Version040 {
|
||||
pubKey := pubKeys[i].SerializeCompressed()
|
||||
xOnlyPubKey, err := schnorr.ParsePubKey(pubKey[1:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing x-only "+
|
||||
"pubkey: %v", err)
|
||||
}
|
||||
|
||||
pubKeys[i] = xOnlyPubKey
|
||||
}
|
||||
}
|
||||
|
||||
if len(privKeys) < 2 {
|
||||
return nil, fmt.Errorf("need at least two signing keys")
|
||||
}
|
||||
|
||||
// Next we'll create MuSig2 sessions for each individual private
|
||||
// signing key.
|
||||
|
|
@ -74,7 +103,7 @@ func MuSig2Sign(version input.MuSig2Version, privKeys []*btcec.PrivateKey,
|
|||
}
|
||||
|
||||
if !haveAllSigs {
|
||||
return nil, fmt.Errorf("combinging MuSig2 signatures " +
|
||||
return nil, fmt.Errorf("combining MuSig2 signatures " +
|
||||
"failed")
|
||||
}
|
||||
|
||||
|
|
|
|||
148
utils/musig_test.go
Normal file
148
utils/musig_test.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// rawKeys returns serialized private keys for the given test seeds.
|
||||
func rawKeys(seeds ...int32) [][32]byte {
|
||||
keys := make([][32]byte, len(seeds))
|
||||
for i, seed := range seeds {
|
||||
privKey, _ := test.CreateKey(seed)
|
||||
copy(keys[i][:], privKey.Serialize())
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// signerPubKeys returns signer public keys derived from the given seeds in the
|
||||
// format expected by the given MuSig2 version.
|
||||
func signerPubKeys(t *testing.T, version input.MuSig2Version,
|
||||
seeds ...int32) []*btcec.PublicKey {
|
||||
|
||||
t.Helper()
|
||||
|
||||
pubKeys := make([]*btcec.PublicKey, len(seeds))
|
||||
for i, seed := range seeds {
|
||||
_, pubKey := test.CreateKey(seed)
|
||||
|
||||
if version == input.MuSig2Version040 {
|
||||
var err error
|
||||
pubKey, err = schnorr.ParsePubKey(
|
||||
schnorr.SerializePubKey(pubKey),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
pubKeys[i] = pubKey
|
||||
}
|
||||
|
||||
return pubKeys
|
||||
}
|
||||
|
||||
// hasOddY returns true if the compressed serialization of the public key uses
|
||||
// the odd-Y prefix.
|
||||
func hasOddY(pubKey *btcec.PublicKey) bool {
|
||||
return pubKey.SerializeCompressed()[0] == 0x03
|
||||
}
|
||||
|
||||
// TestMuSig2SignRejectsSingleSigner ensures the helper fails fast with a clear
|
||||
// error instead of entering an invalid one-party MuSig2 flow.
|
||||
func TestMuSig2SignRejectsSingleSigner(t *testing.T) {
|
||||
_, err := MuSig2Sign(
|
||||
input.MuSig2Version100RC2,
|
||||
rawKeys(1),
|
||||
&input.MuSig2Tweaks{},
|
||||
[32]byte{},
|
||||
)
|
||||
require.ErrorContains(t, err, "need at least two signing keys")
|
||||
}
|
||||
|
||||
// TestMuSig2SignSupportsVersions verifies the helper works with the supported
|
||||
// MuSig2 versions used in Loop.
|
||||
func TestMuSig2SignSupportsVersions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tweaks := &input.MuSig2Tweaks{}
|
||||
msg := [32]byte{1}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
version input.MuSig2Version
|
||||
seeds []int32
|
||||
oddYSigner int32
|
||||
}{
|
||||
{
|
||||
name: testVersionName(input.MuSig2Version040),
|
||||
version: input.MuSig2Version040,
|
||||
seeds: []int32{1, 2},
|
||||
},
|
||||
{
|
||||
name: testVersionName(input.MuSig2Version100RC2),
|
||||
version: input.MuSig2Version100RC2,
|
||||
seeds: []int32{1, 2},
|
||||
},
|
||||
{
|
||||
name: testVersionName(input.MuSig2Version040) +
|
||||
" odd Y",
|
||||
version: input.MuSig2Version040,
|
||||
seeds: []int32{5, 1},
|
||||
oddYSigner: 5,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if testCase.oddYSigner != 0 {
|
||||
_, oddYKey := test.CreateKey(
|
||||
testCase.oddYSigner,
|
||||
)
|
||||
require.True(t, hasOddY(oddYKey))
|
||||
}
|
||||
|
||||
keys := rawKeys(testCase.seeds...)
|
||||
pubKeys := signerPubKeys(
|
||||
t, testCase.version, testCase.seeds...,
|
||||
)
|
||||
|
||||
sigBytes, err := MuSig2Sign(
|
||||
testCase.version, keys, tweaks, msg,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sigBytes, 64)
|
||||
|
||||
sig, err := schnorr.ParseSignature(sigBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
combinedKey, err := input.MuSig2CombineKeys(
|
||||
testCase.version, pubKeys, true, tweaks,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.True(
|
||||
t, sig.Verify(msg[:], combinedKey.FinalKey),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// testVersionName returns a stable subtest name for a MuSig2 version.
|
||||
func testVersionName(version input.MuSig2Version) string {
|
||||
switch version {
|
||||
case input.MuSig2Version040:
|
||||
return "MuSig2 0.4"
|
||||
|
||||
case input.MuSig2Version100RC2:
|
||||
return "MuSig2 1.0RC2"
|
||||
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue