Skip to content

Commit

Permalink
Can sort pubkeys prior to aggregation in musig
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier committed Dec 23, 2024
1 parent 9529a9a commit e4c0829
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 29 deletions.
3 changes: 2 additions & 1 deletion NBitcoin.Tests/Secp256k1Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4038,7 +4038,7 @@ public void musig_det()
{
var keys = Enumerable.Range(0, 5).Select(k => new ECPrivKey(random_scalar_order(), ctx, true)).ToArray();
var pks = keys.Select(k => k.CreatePubKey()).ToArray();
var aggKeys = ECPubKey.MusigAggregate(pks, null);
var aggKeys = ECPubKey.MusigAggregate(pks);
var nonces = new MusigPrivNonce[keys.Length];
var msg = RandomUtils.GetBytes(32);

Expand Down Expand Up @@ -4522,6 +4522,7 @@ public void musig_key_sort_vectors()
var pubkeys2 = GetArray<string>(root["pubkeys"]).Select(p => ECPubKey.Create(Encoders.Hex.DecodeData(p))).ToArray();
var sorted_pubkeys2 = GetArray<string>(root["sorted_pubkeys"]).Select(p => ECPubKey.Create(Encoders.Hex.DecodeData(p))).ToArray();
Array.Sort(pubkeys2);
AssertEx.CollectionEquals(pubkeys2, sorted_pubkeys2);
}

[Fact]
Expand Down
17 changes: 14 additions & 3 deletions NBitcoin/Secp256k1/Musig/ECPubKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,28 @@ internal static Scalar secp256k1_musig_keyaggcoef(MusigContext pre_session, ECPu

const string MusigTag = "KeyAgg coefficient";

public static ECPubKey MusigAggregate(ECPubKey[] pubkeys)
/// <summary>
/// Aggregate the public keys into a single one
/// </summary>
/// <param name="pubkeys">The public keys to aggregate</param>
/// <param name="sort">If true, the pubkeys will be sorted before being aggregated</param>
/// <returns></returns>
public static ECPubKey MusigAggregate(ECPubKey[] pubkeys, bool sort = false)
{
return MusigAggregate(pubkeys, null);
return MusigAggregate(pubkeys, null, sort);
}

internal static ECPubKey MusigAggregate(ECPubKey[] pubkeys, MusigContext? preSession)
internal static ECPubKey MusigAggregate(ECPubKey[] pubkeys, MusigContext? preSession, bool sort)
{
if (pubkeys == null)
throw new ArgumentNullException(nameof(pubkeys));
if (pubkeys.Length is 0)
throw new ArgumentNullException(nameof(pubkeys), "At least one pubkey should be passed");
if (sort)
{
pubkeys = pubkeys.ToArray();
Array.Sort(pubkeys);
}
/* No point on the curve has an X coordinate equal to 0 */
var second_pk_x = FE.Zero;
for (int i = 1; i < pubkeys.Length; i++)
Expand Down
67 changes: 42 additions & 25 deletions NBitcoin/Secp256k1/Musig/MusigContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,46 @@ class MusigContext
private Context ctx;
public ECPubKey? SigningPubKey { get; }

public MusigContext Clone()
{
return new MusigContext(this);
}


/// <inheritdoc cref="MusigContext.MusigContext(ECPubKey[], bool, ReadOnlySpan{byte}, ECPubKey?)"/>
public MusigContext(ECPubKey[] pubkeys, ReadOnlySpan<byte> msg32, ECPubKey? signingPubKey = null)
: this(pubkeys, false, msg32, signingPubKey)
{
}
/// <summary>
/// Create a new musig context
/// </summary>
/// <param name="pubkeys"><inheritdoc cref="ECPubKey.MusigAggregate(ECPubKey[], bool)" path="/param[@name='pubkeys']"/></param>
/// <param name="sort"><inheritdoc cref="ECPubKey.MusigAggregate(ECPubKey[], bool)" path="/param[@name='sort']"/></param>
/// <param name="msg32">The 32 bytes message to sign</param>
/// <param name="signingPubKey">The pubkey of the key that will sign in this context</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public MusigContext(ECPubKey[] pubkeys, bool sort, ReadOnlySpan<byte> msg32, ECPubKey? signingPubKey = null)
{
if (pubkeys == null)
throw new ArgumentNullException(nameof(pubkeys));
if (pubkeys.Length is 0)
throw new ArgumentException(nameof(pubkeys), "There should be at least one pubkey in pubKeys");
if (signingPubKey != null && !pubkeys.Contains(signingPubKey))
throw new InvalidOperationException("The pubkeys do not contain the signing public key");
this.SigningPubKey = signingPubKey;
this.aggregatePubKey = ECPubKey.MusigAggregate(pubkeys, this, sort);
this.ctx = pubkeys[0].ctx;
this.msg32 = msg32.ToArray();
}

/// <summary>
/// Clone a musig context
/// </summary>
/// <param name="musigContext"></param>
/// <exception cref="ArgumentNullException"></exception>
public MusigContext(MusigContext musigContext)
{
if (musigContext == null)
Expand All @@ -73,25 +112,6 @@ public MusigContext(MusigContext musigContext)
SigningPubKey = musigContext.SigningPubKey;
}

public MusigContext Clone()
{
return new MusigContext(this);
}

public MusigContext(ECPubKey[] pubKeys, ReadOnlySpan<byte> msg32, ECPubKey? signingPubKey = null)
{
if (pubKeys == null)
throw new ArgumentNullException(nameof(pubKeys));
if (pubKeys.Length is 0)
throw new ArgumentException(nameof(pubKeys), "There should be at least one pubkey in pubKeys");
if (signingPubKey != null && !pubKeys.Contains(signingPubKey))
throw new InvalidOperationException("The pubkeys do not contain the signing public key");
this.SigningPubKey = signingPubKey;
this.aggregatePubKey = ECPubKey.MusigAggregate(pubKeys, this);
this.ctx = pubKeys[0].ctx;
this.msg32 = msg32.ToArray();
}

/// <summary>
/// Add tweak to the xonly aggregated pubkey
/// </summary>
Expand Down Expand Up @@ -275,12 +295,9 @@ public SecpSchnorrSignature AggregateSignatures(MusigPartialSignature[] partialS
/// <summary>
/// <inheritdoc cref="DeterministicSign(ECPrivKey, byte[])"/>
/// </summary>
/// <param name="privKey"><inheritdoc cref="DeterministicSign(ECPrivKey, byte[])" path="/param/[@name='privKey']"></inheritdoc>/></param>
/// <param name="privKey"><inheritdoc cref="DeterministicSign(ECPrivKey, byte[])" path="/param[@name='privKey']"></inheritdoc></param>
/// <returns></returns>
public (MusigPartialSignature Signature, MusigPubNonce PubNonce) DeterministicSign(ECPrivKey privKey)
{
return DeterministicSign(privKey, null);
}
public (MusigPartialSignature Signature, MusigPubNonce PubNonce) DeterministicSign(ECPrivKey privKey) => DeterministicSign(privKey, null);

/// <summary>
/// <para>Generates a deterministic nonce and sign with it.</para>
Expand All @@ -294,7 +311,7 @@ public SecpSchnorrSignature AggregateSignatures(MusigPartialSignature[] partialS
/// <returns>The partial signature with the deterministic public nonce of this signer</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public (MusigPartialSignature Signature, MusigPubNonce PubNonce) DeterministicSign(ECPrivKey privKey, byte[]? rand = null)
public (MusigPartialSignature Signature, MusigPubNonce PubNonce) DeterministicSign(ECPrivKey privKey, byte[]? rand)
{
var nonce = GenerateDeterministicNonce(privKey, rand);
return (Sign(privKey, nonce), nonce.CreatePubNonce());
Expand Down

0 comments on commit e4c0829

Please sign in to comment.