btcd/btcec/schnorr/musig2/sign.go

649 lines
20 KiB
Go
Raw Normal View History

// Copyright 2013-2022 The btcsuite developers
package musig2
import (
"bytes"
"fmt"
"io"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
var (
// NonceBlindTag is that tag used to construct the value b, which
// blinds the second public nonce of each party.
NonceBlindTag = []byte("MuSig/noncecoef")
// ChallengeHashTag is the tag used to construct the challenge hash
ChallengeHashTag = []byte("BIP0340/challenge")
// ErrNoncePointAtInfinity is returned if during signing, the fully
// combined public nonce is the point at infinity.
ErrNoncePointAtInfinity = fmt.Errorf("signing nonce is the infinity " +
"point")
// ErrPrivKeyZero is returned when the private key for signing is
// actually zero.
ErrPrivKeyZero = fmt.Errorf("priv key is zero")
// ErrPartialSigInvalid is returned when a partial is found to be
// invalid.
ErrPartialSigInvalid = fmt.Errorf("partial signature is invalid")
// ErrSecretNonceZero is returned when a secret nonce is passed in a
// zero.
ErrSecretNonceZero = fmt.Errorf("secret nonce is blank")
)
// infinityPoint is the jacobian representation of the point at infinity.
var infinityPoint btcec.JacobianPoint
// PartialSignature reprints a partial (s-only) musig2 multi-signature. This
// isn't a valid schnorr signature by itself, as it needs to be aggregated
// along with the other partial signatures to be completed.
type PartialSignature struct {
S *btcec.ModNScalar
R *btcec.PublicKey
}
// NewPartialSignature returns a new instances of the partial sig struct.
func NewPartialSignature(s *btcec.ModNScalar,
r *btcec.PublicKey) PartialSignature {
return PartialSignature{
S: s,
R: r,
}
}
// Encode writes a serialized version of the partial signature to the passed
// io.Writer
func (p *PartialSignature) Encode(w io.Writer) error {
var sBytes [32]byte
p.S.PutBytes(&sBytes)
if _, err := w.Write(sBytes[:]); err != nil {
return err
}
return nil
}
// Decode attempts to parse a serialized PartialSignature stored in the passed
// io reader.
func (p *PartialSignature) Decode(r io.Reader) error {
p.S = new(btcec.ModNScalar)
var sBytes [32]byte
if _, err := io.ReadFull(r, sBytes[:]); err != nil {
return nil
}
p.S.SetBytes(&sBytes)
return nil
}
// SignOption is a functional option argument that allows callers to modify the
// way we generate musig2 schnorr signatures.
type SignOption func(*signOptions)
// signOptions houses the set of functional options that can be used to modify
// the method used to generate the musig2 partial signature.
type signOptions struct {
// fastSign determines if we'll skip the check at the end of the
// routine where we attempt to verify the produced signature.
fastSign bool
// sortKeys determines if the set of keys should be sorted before doing
// key aggregation.
sortKeys bool
// tweaks specifies a series of tweaks to be applied to the aggregated
// public key, which also partially carries over into the signing
// process.
tweaks []KeyTweakDesc
// taprootTweak specifies a taproot specific tweak. of the tweaks
// specified above. Normally we'd just apply the raw 32 byte tweak, but
// for taproot, we first need to compute the aggregated key before
// tweaking, and then use it as the internal key. This is required as
// the taproot tweak also commits to the public key, which in this case
// is the aggregated key before the tweak.
taprootTweak []byte
}
// defaultSignOptions returns the default set of signing operations.
func defaultSignOptions() *signOptions {
return &signOptions{}
}
// WithFastSign forces signing to skip the extra verification step at the end.
// Performance sensitive applications may opt to use this option to speed up
// the signing operation.
func WithFastSign() SignOption {
return func(o *signOptions) {
o.fastSign = true
}
}
// WithSortedKeys determines if the set of signing public keys are to be sorted
// or not before doing key aggregation.
func WithSortedKeys() SignOption {
return func(o *signOptions) {
o.sortKeys = true
}
}
// WithTweaks determines if the aggregated public key used should apply a
// series of tweaks before key aggregation.
func WithTweaks(tweaks ...KeyTweakDesc) SignOption {
return func(o *signOptions) {
o.tweaks = tweaks
}
}
// WithTaprootSignTweak allows a caller to specify a tweak that should be used
// in a bip 340 manner when signing. This differs from WithTweaks as the tweak
// will be assumed to always be x-only and the intermediate aggregate key
// before tweaking will be used to generate part of the tweak (as the taproot
// tweak also commits to the internal key).
//
// This option should be used in the taproot context to create a valid
// signature for the keypath spend for taproot, when the output key is actually
// committing to a script path, or some other data.
func WithTaprootSignTweak(scriptRoot []byte) SignOption {
return func(o *signOptions) {
o.taprootTweak = scriptRoot
}
}
// Sign generates a musig2 partial signature given the passed key set, secret
// nonce, public nonce, and private keys. This method returns an error if the
// generated nonces are either too large, or end up mapping to the point at
// infinity.
func Sign(secNonce [SecNonceSize]byte, privKey *btcec.PrivateKey,
combinedNonce [PubNonceSize]byte, pubKeys []*btcec.PublicKey,
msg [32]byte, signOpts ...SignOption) (*PartialSignature, error) {
// First, parse the set of optional signing options.
opts := defaultSignOptions()
for _, option := range signOpts {
option(opts)
}
// Next, we'll parse the public nonces into R1 and R2.
r1, err := btcec.ParsePubKey(
combinedNonce[:btcec.PubKeyBytesLenCompressed],
)
if err != nil {
return nil, err
}
r2, err := btcec.ParsePubKey(
combinedNonce[btcec.PubKeyBytesLenCompressed:],
)
if err != nil {
return nil, err
}
// Compute the hash of all the keys here as we'll need it do aggregate
btcec/schnorr/musig2: optimize signing+verification In this commit, we optimize signing+verification mainly by only computing values once, and reducing allocations when possible. The following optimizations have been implemented: * Use a single buffer allocation in keyHashFingerprint to avoid dynamic buffer growth+re-sizing * Remove the isSecondKey computation and replace that with a single routine that computes the index of the second unique key. * Optimize keyHashFingerprint usage by only computing it once during signing +verification. A further optimization is possible: use the x coordinate of a key for comparisons instead of computing the full sexualision. We need to do the latter atm, as the X() method of the public key struct will allocate more memory as it allocate and sets the buffer in place. The final benchmarks of before and after this commit: benchmark old ns/op new ns/op delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 1227374 1194047 -2.72% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 1217743 1191468 -2.16% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 2755544 2698827 -2.06% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 2754749 2694547 -2.19% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 12382654 10561204 -14.71% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 12260134 10315376 -15.86% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 24832061 22009935 -11.36% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 24650086 21022833 -14.71% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 1485787 1473377 -0.84% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 1447275 1465139 +1.23% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 12503482 10672618 -14.64% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 12388289 10581398 -14.59% BenchmarkCombineSigs/num_signers=10-8 0.00 0.00 +0.00% BenchmarkCombineSigs/num_signers=100-8 0.00 0.00 -1.95% BenchmarkAggregateNonces/num_signers=10-8 0.00 0.00 -0.76% BenchmarkAggregateNonces/num_signers=100-8 0.00 0.00 +1.13% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0.00 0.00 -0.09% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0.00 0.01 +559.94% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0.01 0.01 -11.30% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0.01 0.01 -11.66% benchmark old allocs new allocs delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 458 269 -41.27% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 409 222 -45.72% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 892 524 -41.26% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 841 467 -44.47% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 14366 3089 -78.50% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 13143 1842 -85.98% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 27596 4964 -82.01% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 26309 3707 -85.91% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 13164 1863 -85.85% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 13164 1863 -85.85% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 27854 14878 -46.59% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 25508 12605 -50.58% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 54982 29476 -46.39% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 52581 26805 -49.02% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 1880138 166996 -91.12% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 1820561 106295 -94.16% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 3706291 275344 -92.57% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 3642725 214122 -94.12% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 26995 14078 -47.85% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 26980 14078 -47.82% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 1822043 107767 -94.09% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 1822046 107752 -94.09% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00%
2022-03-10 18:47:52 -08:00
// the keys and also at the final step of signing.
keysHash := keyHashFingerprint(pubKeys, opts.sortKeys)
uniqueKeyIndex := secondUniqueKeyIndex(pubKeys, opts.sortKeys)
keyAggOpts := []KeyAggOption{
WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
}
if opts.taprootTweak != nil {
keyAggOpts = append(
keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
)
} else {
keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
}
btcec/schnorr/musig2: optimize signing+verification In this commit, we optimize signing+verification mainly by only computing values once, and reducing allocations when possible. The following optimizations have been implemented: * Use a single buffer allocation in keyHashFingerprint to avoid dynamic buffer growth+re-sizing * Remove the isSecondKey computation and replace that with a single routine that computes the index of the second unique key. * Optimize keyHashFingerprint usage by only computing it once during signing +verification. A further optimization is possible: use the x coordinate of a key for comparisons instead of computing the full sexualision. We need to do the latter atm, as the X() method of the public key struct will allocate more memory as it allocate and sets the buffer in place. The final benchmarks of before and after this commit: benchmark old ns/op new ns/op delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 1227374 1194047 -2.72% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 1217743 1191468 -2.16% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 2755544 2698827 -2.06% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 2754749 2694547 -2.19% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 12382654 10561204 -14.71% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 12260134 10315376 -15.86% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 24832061 22009935 -11.36% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 24650086 21022833 -14.71% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 1485787 1473377 -0.84% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 1447275 1465139 +1.23% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 12503482 10672618 -14.64% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 12388289 10581398 -14.59% BenchmarkCombineSigs/num_signers=10-8 0.00 0.00 +0.00% BenchmarkCombineSigs/num_signers=100-8 0.00 0.00 -1.95% BenchmarkAggregateNonces/num_signers=10-8 0.00 0.00 -0.76% BenchmarkAggregateNonces/num_signers=100-8 0.00 0.00 +1.13% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0.00 0.00 -0.09% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0.00 0.01 +559.94% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0.01 0.01 -11.30% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0.01 0.01 -11.66% benchmark old allocs new allocs delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 458 269 -41.27% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 409 222 -45.72% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 892 524 -41.26% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 841 467 -44.47% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 14366 3089 -78.50% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 13143 1842 -85.98% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 27596 4964 -82.01% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 26309 3707 -85.91% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 13164 1863 -85.85% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 13164 1863 -85.85% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 27854 14878 -46.59% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 25508 12605 -50.58% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 54982 29476 -46.39% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 52581 26805 -49.02% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 1880138 166996 -91.12% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 1820561 106295 -94.16% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 3706291 275344 -92.57% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 3642725 214122 -94.12% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 26995 14078 -47.85% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 26980 14078 -47.82% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 1822043 107767 -94.09% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 1822046 107752 -94.09% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00%
2022-03-10 18:47:52 -08:00
// Next we'll construct the aggregated public key based on the set of
// signers.
combinedKey, parityAcc, _, err := AggregateKeys(
pubKeys, opts.sortKeys, keyAggOpts...,
btcec/schnorr/musig2: optimize signing+verification In this commit, we optimize signing+verification mainly by only computing values once, and reducing allocations when possible. The following optimizations have been implemented: * Use a single buffer allocation in keyHashFingerprint to avoid dynamic buffer growth+re-sizing * Remove the isSecondKey computation and replace that with a single routine that computes the index of the second unique key. * Optimize keyHashFingerprint usage by only computing it once during signing +verification. A further optimization is possible: use the x coordinate of a key for comparisons instead of computing the full sexualision. We need to do the latter atm, as the X() method of the public key struct will allocate more memory as it allocate and sets the buffer in place. The final benchmarks of before and after this commit: benchmark old ns/op new ns/op delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 1227374 1194047 -2.72% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 1217743 1191468 -2.16% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 2755544 2698827 -2.06% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 2754749 2694547 -2.19% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 12382654 10561204 -14.71% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 12260134 10315376 -15.86% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 24832061 22009935 -11.36% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 24650086 21022833 -14.71% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 1485787 1473377 -0.84% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 1447275 1465139 +1.23% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 12503482 10672618 -14.64% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 12388289 10581398 -14.59% BenchmarkCombineSigs/num_signers=10-8 0.00 0.00 +0.00% BenchmarkCombineSigs/num_signers=100-8 0.00 0.00 -1.95% BenchmarkAggregateNonces/num_signers=10-8 0.00 0.00 -0.76% BenchmarkAggregateNonces/num_signers=100-8 0.00 0.00 +1.13% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0.00 0.00 -0.09% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0.00 0.01 +559.94% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0.01 0.01 -11.30% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0.01 0.01 -11.66% benchmark old allocs new allocs delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 458 269 -41.27% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 409 222 -45.72% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 892 524 -41.26% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 841 467 -44.47% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 14366 3089 -78.50% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 13143 1842 -85.98% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 27596 4964 -82.01% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 26309 3707 -85.91% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 13164 1863 -85.85% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 13164 1863 -85.85% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 27854 14878 -46.59% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 25508 12605 -50.58% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 54982 29476 -46.39% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 52581 26805 -49.02% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 1880138 166996 -91.12% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 1820561 106295 -94.16% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 3706291 275344 -92.57% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 3642725 214122 -94.12% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 26995 14078 -47.85% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 26980 14078 -47.82% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 1822043 107767 -94.09% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 1822046 107752 -94.09% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00%
2022-03-10 18:47:52 -08:00
)
if err != nil {
return nil, err
}
// Next we'll compute the value b, that blinds our second public
// nonce:
// * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
var (
nonceMsgBuf bytes.Buffer
nonceBlinder btcec.ModNScalar
)
nonceMsgBuf.Write(combinedNonce[:])
nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
nonceMsgBuf.Write(msg[:])
nonceBlindHash := chainhash.TaggedHash(
NonceBlindTag, nonceMsgBuf.Bytes(),
)
nonceBlinder.SetByteSlice(nonceBlindHash[:])
var nonce, r1J, r2J btcec.JacobianPoint
r1.AsJacobian(&r1J)
r2.AsJacobian(&r2J)
// With our nonce blinding value, we'll now combine both the public
// nonces, using the blinding factor to tweak the second nonce:
// * R = R_1 + b*R_2
btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
btcec.AddNonConst(&r1J, &r2J, &nonce)
// If the combined nonce it eh point at infinity, then we'll bail out.
if nonce == infinityPoint {
return nil, ErrNoncePointAtInfinity
}
// Next we'll parse out our two secret nonces, which we'll be using in
// the core signing process below.
var k1, k2 btcec.ModNScalar
k1.SetByteSlice(secNonce[:btcec.PrivKeyBytesLen])
k2.SetByteSlice(secNonce[btcec.PrivKeyBytesLen:])
if k1.IsZero() || k2.IsZero() {
return nil, ErrSecretNonceZero
}
nonce.ToAffine()
nonceKey := btcec.NewPublicKey(&nonce.X, &nonce.Y)
// If the nonce R has an odd y coordinate, then we'll negate both our
// secret nonces.
if nonce.Y.IsOdd() {
k1.Negate()
k2.Negate()
}
privKeyScalar := privKey.Key
if privKeyScalar.IsZero() {
return nil, ErrPrivKeyZero
}
pubKey := privKey.PubKey()
pubKeyYIsOdd := func() bool {
pubKeyBytes := pubKey.SerializeCompressed()
return pubKeyBytes[0] == secp.PubKeyFormatCompressedOdd
}()
combinedKeyYIsOdd := func() bool {
combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
return combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd
}()
// Next we'll compute our two parity factors for Q the combined public
// key, and P, the public key we're signing with. If the keys are odd,
// then we'll negate them.
parityCombinedKey := new(btcec.ModNScalar).SetInt(1)
paritySignKey := new(btcec.ModNScalar).SetInt(1)
if combinedKeyYIsOdd {
parityCombinedKey.Negate()
}
if pubKeyYIsOdd {
paritySignKey.Negate()
}
// Before we sign below, we'll multiply by our various parity factors
// to ensure that the signing key is properly negated (if necessary):
// * d = gvâ‹…gaccvâ‹…gpâ‹…d'
privKeyScalar.Mul(parityCombinedKey).Mul(paritySignKey).Mul(parityAcc)
// Next we'll create the challenge hash that commits to the combined
// nonce, combined public key and also the message:
// * e = H(tag=ChallengeHashTag, R || Q || m) mod n
var challengeMsg bytes.Buffer
challengeMsg.Write(schnorr.SerializePubKey(nonceKey))
challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
challengeMsg.Write(msg[:])
challengeBytes := chainhash.TaggedHash(
ChallengeHashTag, challengeMsg.Bytes(),
)
var e btcec.ModNScalar
e.SetByteSlice(challengeBytes[:])
// Next, we'll compute a, our aggregation coefficient for the key that
// we're signing with.
a := aggregationCoefficient(pubKeys, pubKey, keysHash, uniqueKeyIndex)
// With mu constructed, we can finally generate our partial signature
// as: s = (k1_1 + b*k_2 + e*a*d) mod n.
s := new(btcec.ModNScalar)
s.Add(&k1).Add(k2.Mul(&nonceBlinder)).Add(e.Mul(a).Mul(&privKeyScalar))
sig := NewPartialSignature(s, nonceKey)
// If we're not in fast sign mode, then we'll also validate our partial
// signature.
if !opts.fastSign {
pubNonce := secNonceToPubNonce(secNonce)
sigValid := sig.Verify(
pubNonce, combinedNonce, pubKeys, pubKey, msg,
signOpts...,
)
if !sigValid {
return nil, fmt.Errorf("sig is invalid!")
}
}
return &sig, nil
}
// Verify implements partial signature verification given the public nonce for
// the signer, aggregate nonce, signer set and finally the message being
// signed.
func (p *PartialSignature) Verify(pubNonce [PubNonceSize]byte,
combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
signingKey *btcec.PublicKey, msg [32]byte, signOpts ...SignOption) bool {
pubKey := schnorr.SerializePubKey(signingKey)
return verifyPartialSig(
p, pubNonce, combinedNonce, keySet, pubKey, msg, signOpts...,
) == nil
}
// verifyPartialSig attempts to verify a partial schnorr signature given the
// necessary parameters. This is the internal version of Verify that returns
// detailed errors. signed.
func verifyPartialSig(partialSig *PartialSignature, pubNonce [PubNonceSize]byte,
combinedNonce [PubNonceSize]byte, keySet []*btcec.PublicKey,
pubKey []byte, msg [32]byte, signOpts ...SignOption) error {
opts := defaultSignOptions()
for _, option := range signOpts {
option(opts)
}
// First we'll map the internal partial signature back into something
// we can manipulate.
s := partialSig.S
// Next we'll parse out the two public nonces into something we can
// use.
//
// TODO(roasbeef): consolidate, new method
r1, err := btcec.ParsePubKey(
combinedNonce[:btcec.PubKeyBytesLenCompressed],
)
if err != nil {
return err
}
r2, err := btcec.ParsePubKey(
combinedNonce[btcec.PubKeyBytesLenCompressed:],
)
if err != nil {
return err
}
// Compute the hash of all the keys here as we'll need it do aggregate
btcec/schnorr/musig2: optimize signing+verification In this commit, we optimize signing+verification mainly by only computing values once, and reducing allocations when possible. The following optimizations have been implemented: * Use a single buffer allocation in keyHashFingerprint to avoid dynamic buffer growth+re-sizing * Remove the isSecondKey computation and replace that with a single routine that computes the index of the second unique key. * Optimize keyHashFingerprint usage by only computing it once during signing +verification. A further optimization is possible: use the x coordinate of a key for comparisons instead of computing the full sexualision. We need to do the latter atm, as the X() method of the public key struct will allocate more memory as it allocate and sets the buffer in place. The final benchmarks of before and after this commit: benchmark old ns/op new ns/op delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 1227374 1194047 -2.72% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 1217743 1191468 -2.16% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 2755544 2698827 -2.06% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 2754749 2694547 -2.19% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 12382654 10561204 -14.71% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 12260134 10315376 -15.86% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 24832061 22009935 -11.36% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 24650086 21022833 -14.71% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 1485787 1473377 -0.84% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 1447275 1465139 +1.23% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 12503482 10672618 -14.64% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 12388289 10581398 -14.59% BenchmarkCombineSigs/num_signers=10-8 0.00 0.00 +0.00% BenchmarkCombineSigs/num_signers=100-8 0.00 0.00 -1.95% BenchmarkAggregateNonces/num_signers=10-8 0.00 0.00 -0.76% BenchmarkAggregateNonces/num_signers=100-8 0.00 0.00 +1.13% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0.00 0.00 -0.09% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0.00 0.01 +559.94% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0.01 0.01 -11.30% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0.01 0.01 -11.66% benchmark old allocs new allocs delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 458 269 -41.27% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 409 222 -45.72% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 892 524 -41.26% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 841 467 -44.47% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 14366 3089 -78.50% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 13143 1842 -85.98% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 27596 4964 -82.01% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 26309 3707 -85.91% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 13164 1863 -85.85% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 13164 1863 -85.85% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 27854 14878 -46.59% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 25508 12605 -50.58% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 54982 29476 -46.39% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 52581 26805 -49.02% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 1880138 166996 -91.12% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 1820561 106295 -94.16% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 3706291 275344 -92.57% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 3642725 214122 -94.12% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 26995 14078 -47.85% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 26980 14078 -47.82% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 1822043 107767 -94.09% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 1822046 107752 -94.09% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00%
2022-03-10 18:47:52 -08:00
// the keys and also at the final step of verification.
keysHash := keyHashFingerprint(keySet, opts.sortKeys)
uniqueKeyIndex := secondUniqueKeyIndex(keySet, opts.sortKeys)
btcec/schnorr/musig2: optimize signing+verification In this commit, we optimize signing+verification mainly by only computing values once, and reducing allocations when possible. The following optimizations have been implemented: * Use a single buffer allocation in keyHashFingerprint to avoid dynamic buffer growth+re-sizing * Remove the isSecondKey computation and replace that with a single routine that computes the index of the second unique key. * Optimize keyHashFingerprint usage by only computing it once during signing +verification. A further optimization is possible: use the x coordinate of a key for comparisons instead of computing the full sexualision. We need to do the latter atm, as the X() method of the public key struct will allocate more memory as it allocate and sets the buffer in place. The final benchmarks of before and after this commit: benchmark old ns/op new ns/op delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 1227374 1194047 -2.72% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 1217743 1191468 -2.16% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 2755544 2698827 -2.06% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 2754749 2694547 -2.19% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 12382654 10561204 -14.71% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 12260134 10315376 -15.86% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 24832061 22009935 -11.36% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 24650086 21022833 -14.71% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 1485787 1473377 -0.84% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 1447275 1465139 +1.23% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 12503482 10672618 -14.64% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 12388289 10581398 -14.59% BenchmarkCombineSigs/num_signers=10-8 0.00 0.00 +0.00% BenchmarkCombineSigs/num_signers=100-8 0.00 0.00 -1.95% BenchmarkAggregateNonces/num_signers=10-8 0.00 0.00 -0.76% BenchmarkAggregateNonces/num_signers=100-8 0.00 0.00 +1.13% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0.00 0.00 -0.09% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0.00 0.01 +559.94% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0.01 0.01 -11.30% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0.01 0.01 -11.66% benchmark old allocs new allocs delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 458 269 -41.27% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 409 222 -45.72% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 892 524 -41.26% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 841 467 -44.47% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 14366 3089 -78.50% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 13143 1842 -85.98% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 27596 4964 -82.01% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 26309 3707 -85.91% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 13164 1863 -85.85% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 13164 1863 -85.85% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 27854 14878 -46.59% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 25508 12605 -50.58% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 54982 29476 -46.39% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 52581 26805 -49.02% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 1880138 166996 -91.12% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 1820561 106295 -94.16% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 3706291 275344 -92.57% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 3642725 214122 -94.12% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 26995 14078 -47.85% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 26980 14078 -47.82% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 1822043 107767 -94.09% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 1822046 107752 -94.09% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00%
2022-03-10 18:47:52 -08:00
keyAggOpts := []KeyAggOption{
WithKeysHash(keysHash), WithUniqueKeyIndex(uniqueKeyIndex),
}
if opts.taprootTweak != nil {
keyAggOpts = append(
keyAggOpts, WithTaprootKeyTweak(opts.taprootTweak),
)
} else {
keyAggOpts = append(keyAggOpts, WithKeyTweaks(opts.tweaks...))
}
// Next we'll construct the aggregated public key based on the set of
// signers.
combinedKey, parityAcc, _, err := AggregateKeys(
keySet, opts.sortKeys, keyAggOpts...,
btcec/schnorr/musig2: optimize signing+verification In this commit, we optimize signing+verification mainly by only computing values once, and reducing allocations when possible. The following optimizations have been implemented: * Use a single buffer allocation in keyHashFingerprint to avoid dynamic buffer growth+re-sizing * Remove the isSecondKey computation and replace that with a single routine that computes the index of the second unique key. * Optimize keyHashFingerprint usage by only computing it once during signing +verification. A further optimization is possible: use the x coordinate of a key for comparisons instead of computing the full sexualision. We need to do the latter atm, as the X() method of the public key struct will allocate more memory as it allocate and sets the buffer in place. The final benchmarks of before and after this commit: benchmark old ns/op new ns/op delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 1227374 1194047 -2.72% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 1217743 1191468 -2.16% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 2755544 2698827 -2.06% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 2754749 2694547 -2.19% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 12382654 10561204 -14.71% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 12260134 10315376 -15.86% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 24832061 22009935 -11.36% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 24650086 21022833 -14.71% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 1485787 1473377 -0.84% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 1447275 1465139 +1.23% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 12503482 10672618 -14.64% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 12388289 10581398 -14.59% BenchmarkCombineSigs/num_signers=10-8 0.00 0.00 +0.00% BenchmarkCombineSigs/num_signers=100-8 0.00 0.00 -1.95% BenchmarkAggregateNonces/num_signers=10-8 0.00 0.00 -0.76% BenchmarkAggregateNonces/num_signers=100-8 0.00 0.00 +1.13% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0.00 0.00 -0.09% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0.00 0.01 +559.94% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0.01 0.01 -11.30% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0.01 0.01 -11.66% benchmark old allocs new allocs delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 458 269 -41.27% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 409 222 -45.72% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 892 524 -41.26% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 841 467 -44.47% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 14366 3089 -78.50% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 13143 1842 -85.98% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 27596 4964 -82.01% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 26309 3707 -85.91% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 430 243 -43.49% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 13164 1863 -85.85% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 13164 1863 -85.85% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=true-8 27854 14878 -46.59% BenchmarkPartialSign/num_signers=10/fast_sign=true/sort=false-8 25508 12605 -50.58% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=true-8 54982 29476 -46.39% BenchmarkPartialSign/num_signers=10/fast_sign=false/sort=false-8 52581 26805 -49.02% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=true-8 1880138 166996 -91.12% BenchmarkPartialSign/num_signers=100/fast_sign=true/sort=false-8 1820561 106295 -94.16% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=true-8 3706291 275344 -92.57% BenchmarkPartialSign/num_signers=100/fast_sign=false/sort=false-8 3642725 214122 -94.12% BenchmarkPartialVerify/sort_keys=true/num_signers=10-8 26995 14078 -47.85% BenchmarkPartialVerify/sort_keys=false/num_signers=10-8 26980 14078 -47.82% BenchmarkPartialVerify/sort_keys=true/num_signers=100-8 1822043 107767 -94.09% BenchmarkPartialVerify/sort_keys=false/num_signers=100-8 1822046 107752 -94.09% BenchmarkCombineSigs/num_signers=10-8 0 0 +0.00% BenchmarkCombineSigs/num_signers=100-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=10-8 0 0 +0.00% BenchmarkAggregateNonces/num_signers=100-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=10/sort_keys=false-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=true-8 0 0 +0.00% BenchmarkAggregateKeys/num_signers=100/sort_keys=false-8 0 0 +0.00%
2022-03-10 18:47:52 -08:00
)
if err != nil {
return err
}
// Next we'll compute the value b, that blinds our second public
// nonce:
// * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
var (
nonceMsgBuf bytes.Buffer
nonceBlinder btcec.ModNScalar
)
nonceMsgBuf.Write(combinedNonce[:])
nonceMsgBuf.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
nonceMsgBuf.Write(msg[:])
nonceBlindHash := chainhash.TaggedHash(NonceBlindTag, nonceMsgBuf.Bytes())
nonceBlinder.SetByteSlice(nonceBlindHash[:])
var nonce, r1J, r2J btcec.JacobianPoint
r1.AsJacobian(&r1J)
r2.AsJacobian(&r2J)
// With our nonce blinding value, we'll now combine both the public
// nonces, using the blinding factor to tweak the second nonce:
// * R = R_1 + b*R_2
btcec.ScalarMultNonConst(&nonceBlinder, &r2J, &r2J)
btcec.AddNonConst(&r1J, &r2J, &nonce)
// Next, we'll parse out the set of public nonces this signer used to
// generate the signature.
pubNonce1, err := btcec.ParsePubKey(
pubNonce[:btcec.PubKeyBytesLenCompressed],
)
if err != nil {
return err
}
pubNonce2, err := btcec.ParsePubKey(
pubNonce[btcec.PubKeyBytesLenCompressed:],
)
if err != nil {
return err
}
// We'll perform a similar aggregation and blinding operator as we did
// above for the combined nonces: R' = R_1' + b*R_2'.
var pubNonceJ, pubNonce1J, pubNonce2J btcec.JacobianPoint
pubNonce1.AsJacobian(&pubNonce1J)
pubNonce2.AsJacobian(&pubNonce2J)
btcec.ScalarMultNonConst(&nonceBlinder, &pubNonce2J, &pubNonce2J)
btcec.AddNonConst(&pubNonce1J, &pubNonce2J, &pubNonceJ)
nonce.ToAffine()
// If the combined nonce used in the challenge hash has an odd y
// coordinate, then we'll negate our final public nonce.
if nonce.Y.IsOdd() {
pubNonceJ.ToAffine()
pubNonceJ.Y.Negate(1)
pubNonceJ.Y.Normalize()
}
// Next we'll create the challenge hash that commits to the combined
// nonce, combined public key and also the message:
// * e = H(tag=ChallengeHashTag, R || Q || m) mod n
var challengeMsg bytes.Buffer
challengeMsg.Write(schnorr.SerializePubKey(btcec.NewPublicKey(
&nonce.X, &nonce.Y,
)))
challengeMsg.Write(schnorr.SerializePubKey(combinedKey.FinalKey))
challengeMsg.Write(msg[:])
challengeBytes := chainhash.TaggedHash(
ChallengeHashTag, challengeMsg.Bytes(),
)
var e btcec.ModNScalar
e.SetByteSlice(challengeBytes[:])
signingKey, err := schnorr.ParsePubKey(pubKey)
if err != nil {
return err
}
// Next, we'll compute a, our aggregation coefficient for the key that
// we're signing with.
a := aggregationCoefficient(keySet, signingKey, keysHash, uniqueKeyIndex)
// If the combined key has an odd y coordinate, then we'll negate
// parity factor for the signing key.
paritySignKey := new(btcec.ModNScalar).SetInt(1)
combinedKeyBytes := combinedKey.FinalKey.SerializeCompressed()
if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
paritySignKey.Negate()
}
// Next, we'll construct the final parity factor by multiplying the
// sign key parity factor with the accumulated parity factor for all
// the keys.
finalParityFactor := paritySignKey.Mul(parityAcc)
// Now we'll multiply the parity factor by our signing key, which'll
// take care of the amount of negation needed.
var signKeyJ btcec.JacobianPoint
signingKey.AsJacobian(&signKeyJ)
btcec.ScalarMultNonConst(finalParityFactor, &signKeyJ, &signKeyJ)
// In the final set, we'll check that: s*G == R' + e*a*P.
var sG, rP btcec.JacobianPoint
btcec.ScalarBaseMultNonConst(s, &sG)
btcec.ScalarMultNonConst(e.Mul(a), &signKeyJ, &rP)
btcec.AddNonConst(&rP, &pubNonceJ, &rP)
sG.ToAffine()
rP.ToAffine()
if sG != rP {
return ErrPartialSigInvalid
}
return nil
}
// CombineOption is a functional option argument that allows callers to modify the
// way we combine musig2 schnorr signatures.
type CombineOption func(*combineOptions)
// combineOptions houses the set of functional options that can be used to
// modify the method used to combine the musig2 partial signatures.
type combineOptions struct {
msg [32]byte
combinedKey *btcec.PublicKey
tweakAcc *btcec.ModNScalar
}
// defaultCombineOptions returns the default set of signing operations.
func defaultCombineOptions() *combineOptions {
return &combineOptions{}
}
// WithTweakedCombine is a functional option that allows callers to specify
// that the signature was produced using a tweaked aggregated public key. In
// order to properly aggregate the partial signatures, the caller must specify
// enough information to reconstruct the challenge, and also the final
// accumulated tweak value.
func WithTweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
tweaks []KeyTweakDesc, sort bool) CombineOption {
return func(o *combineOptions) {
combinedKey, _, tweakAcc, _ := AggregateKeys(
keys, sort, WithKeyTweaks(tweaks...),
)
o.msg = msg
o.combinedKey = combinedKey.FinalKey
o.tweakAcc = tweakAcc
}
}
// WithTaprootTweakedCombine is similar to the WithTweakedCombine option, but
// assumes a BIP 341 context where the final tweaked key is to be used as the
// output key, where the internal key is the aggregated key pre-tweak.
//
// This option should be used over WithTweakedCombine when attempting to
// aggregate signatures for a top-level taproot keysepnd, where the output key
// commits to a script root.
func WithTaprootTweakedCombine(msg [32]byte, keys []*btcec.PublicKey,
scriptRoot []byte, sort bool) CombineOption {
return func(o *combineOptions) {
combinedKey, _, tweakAcc, _ := AggregateKeys(
keys, sort, WithTaprootKeyTweak(scriptRoot),
)
o.msg = msg
o.combinedKey = combinedKey.FinalKey
o.tweakAcc = tweakAcc
}
}
// CombineSigs combines the set of public keys given the final aggregated
// nonce, and the series of partial signatures for each nonce.
func CombineSigs(combinedNonce *btcec.PublicKey,
partialSigs []*PartialSignature,
combineOpts ...CombineOption) *schnorr.Signature {
// First, parse the set of optional combine options.
opts := defaultCombineOptions()
for _, option := range combineOpts {
option(opts)
}
// If signer keys and tweaks are specified, then we need to carry out
// some intermediate steps before we can combine the signature.
var tweakProduct *btcec.ModNScalar
if opts.combinedKey != nil && opts.tweakAcc != nil {
// Next, we'll construct the parity factor of the combined key,
// negating it if the combined key has an even y coordinate.
parityFactor := new(btcec.ModNScalar).SetInt(1)
combinedKeyBytes := opts.combinedKey.SerializeCompressed()
if combinedKeyBytes[0] == secp.PubKeyFormatCompressedOdd {
parityFactor.Negate()
}
// Next we'll reconstruct e the challenge has based on the
// nonce and combined public key.
// * e = H(tag=ChallengeHashTag, R || Q || m) mod n
var challengeMsg bytes.Buffer
challengeMsg.Write(schnorr.SerializePubKey(combinedNonce))
challengeMsg.Write(schnorr.SerializePubKey(opts.combinedKey))
challengeMsg.Write(opts.msg[:])
challengeBytes := chainhash.TaggedHash(
ChallengeHashTag, challengeMsg.Bytes(),
)
var e btcec.ModNScalar
e.SetByteSlice(challengeBytes[:])
tweakProduct = new(btcec.ModNScalar).Set(&e)
tweakProduct.Mul(opts.tweakAcc).Mul(parityFactor)
}
// Finally, the tweak factor also needs to be re-computed as well.
var combinedSig btcec.ModNScalar
for _, partialSig := range partialSigs {
combinedSig.Add(partialSig.S)
}
// If the tweak product was set above, then we'll need to add the value
// at the very end in order to produce a valid signature under the
// final tweaked key.
if tweakProduct != nil {
combinedSig.Add(tweakProduct)
}
// TODO(roasbeef): less verbose way to get the x coord...
var nonceJ btcec.JacobianPoint
combinedNonce.AsJacobian(&nonceJ)
nonceJ.ToAffine()
return schnorr.NewSignature(&nonceJ.X, &combinedSig)
}