Struct musig2::KeyAggContext

source ·
pub struct KeyAggContext { /* private fields */ }
Expand description

Represents an aggregated and tweaked public key.

A set of pubkeys can be aggregated into a KeyAggContext which allows co-signers to cooperatively sign data.

KeyAggContext is essentially a sequence of pubkeys and tweaks which determine a final aggregated key, with which the whole cohort can cooperatively sign messages.

See KeyAggContext::with_tweak to learn more about tweaking.

Implementations§

source§

impl KeyAggContext

source

pub fn new<I, P>(pubkeys: I) -> Result<Self, KeyAggError>
where I: IntoIterator<Item = P>, P: Into<Point>,

Constructs a key aggregation context for a given set of pubkeys. The order in which the pubkeys are presented by the iterator will be preserved. A specific ordering of pubkeys will uniquely determine the aggregated public key.

If the same keys are provided again in a different sorting order, a different aggregated pubkey will result. We recommended to sort keys ahead of time in some deterministic fashion before constructing a KeyAggContext.

use secp256k1::PublicKey;
use musig2::KeyAggContext;

let mut pubkeys: [PublicKey; 3] = [
    "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"
        .parse()
        .unwrap(),
    "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
        .parse()
        .unwrap(),
    "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66"
        .parse()
        .unwrap(),
];

let key_agg_ctx = KeyAggContext::new(pubkeys)
    .expect("error aggregating pubkeys");

pubkeys.sort();
let sorted_key_agg_ctx = KeyAggContext::new(pubkeys).unwrap();

let pk: PublicKey = key_agg_ctx.aggregated_pubkey();
let pk_sorted: PublicKey = sorted_key_agg_ctx.aggregated_pubkey();
assert_ne!(pk, pk_sorted);

Multiple copies of the same public key are also accepted. They will be aggregated together and all signers will be expected to provide valid signatures from their key.

Signers will be identified by their index from zero. The first key returned from the pubkeys iterator will be signer 0. The second key will be index 1, and so on. It is important that the caller can clearly identify every signer, so that they know who to blame if a signing contribution (e.g. a partial signature) is invalid.

source

pub fn with_tweak( self, tweak: impl Into<Scalar>, is_xonly: bool ) -> Result<Self, TweakError>

Tweak the key aggregation context with a specific scalar tweak value.

‘Tweaking’ is the practice of committing a key to an agreed-upon scalar value, such as a SHA256 hash. In Bitcoin contexts, this is used for taproot script commitments, or BIP32 key derivation.

Signatures created using the resulting tweaked key aggregation context will be bound to this tweak value.

A verifier can later prove that the signer(s) committed to this value if the tweak value was itself generated by committing to the public key, e.g. by hashing the aggregated public key.

The is_xonly argument determines whether the tweak should be applied to the plain aggregated pubkey, or to the even-parity (i.e. x-only) aggregated pubkey. is_xonly should be true for applying Bitcoin taproot commitments, and false for applying BIP32 key derivation tweaks.

Returns an error if the tweaked public key would be the point at infinity.

use secp256k1::{PublicKey, SecretKey};
use musig2::KeyAggContext;

let pubkeys = [
    "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"
        .parse::<PublicKey>()
        .unwrap(),
    "023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66"
        .parse::<PublicKey>()
        .unwrap(),
];

let key_agg_ctx = KeyAggContext::new(pubkeys)
    .unwrap()
    .with_tweak(
        "7931676703c0865d8b502dcdf1d956e86503796cfeabe33d12a918fbf408da05"
            .parse::<SecretKey>()
            .unwrap(),
        false
    )
    .unwrap();

let aggregated_pubkey: PublicKey = key_agg_ctx.aggregated_pubkey_untweaked();

assert_eq!(
    aggregated_pubkey.to_string(),
    "0385eb6101982e142dba553cae437d08a82880fe9a22889c997f8e415a61b7a2d5"
);
source

pub fn with_tweaks<S, I>(self, tweaks: I) -> Result<Self, TweakError>
where I: IntoIterator<Item = (S, bool)>, S: Into<Scalar>,

Iteratively applies tweaks to the aggregated pubkey. See KeyAggContext::with_tweak.

source

pub fn with_plain_tweak( self, tweak: impl Into<Scalar> ) -> Result<Self, TweakError>

Same as self.with_tweak(tweak, false). See KeyAggContext::with_tweak.

source

pub fn with_xonly_tweak( self, tweak: impl Into<Scalar> ) -> Result<Self, TweakError>

Same as self.with_tweak(tweak, true). See KeyAggContext::with_tweak.

source

pub fn with_taproot_tweak( self, merkle_root: &[u8; 32] ) -> Result<Self, TweakError>

Tweak the key aggregation context with the given tapscript merkle tree root hash.

This is used to commit the key aggregation context to a specific tree of Bitcoin taproot scripts, determined by the given merkle_root hash. Computing the merkle tree root is outside the scope of this package. See BIP341 for details of how tapscript merkle trees are constructed.

The tweak value t is computed as:

prefix = sha256(b"TapTweak")
tweak_hash = sha256(
    prefix,
    prefix,
    self.aggregated_pubkey().serialize_xonly(),
    merkle_root
)
t = int(tweak_hash)

Note that the current tweaked aggregated pubkey is hashed, not the plain untweaked pubkey.

source

pub fn with_unspendable_taproot_tweak(self) -> Result<Self, TweakError>

Tweak the key aggregation context with an empty unspendable merkle root.

This allows a 3rd party observer (who doesn’t know the constituent musig group member keys) to verify, given the untweaked group key, that the tweaked group key does not commit to any hidden tapscript trees. See BIP341 for more info.

The tweak value t is computed as:

prefix = sha256(b"TapTweak")
tweak_hash = sha256(
    prefix,
    prefix,
    self.aggregated_pubkey().serialize_xonly(),
)
t = int(tweak_hash)

Note that the current tweaked aggregated pubkey is hashed, not the plain untweaked pubkey.

source

pub fn aggregated_pubkey<T: From<Point>>(&self) -> T

Returns the aggregated public key, converted to a given type.

use secp256k1::PublicKey;
use musig2::KeyAggContext;

let pubkeys: Vec<PublicKey> = vec![
    /* ... */
];

let key_agg_ctx = KeyAggContext::new(pubkeys).unwrap();
let aggregated_pubkey: PublicKey = key_agg_ctx.aggregated_pubkey();
assert_eq!(
    aggregated_pubkey.to_string(),
    "0290539eede565f5d054f32cc0c220126889ed1e5d193baf15aef344fe59d4610c"
)

If any tweaks have been applied to the KeyAggContext, the the pubkey returned by this method will be the tweaked aggregate public key, and not the plain aggregated key.

source

pub fn aggregated_pubkey_untweaked<T: From<Point>>(&self) -> T

Returns the aggregated pubkey without any tweaks.

use secp256k1::{PublicKey, SecretKey};
use musig2::KeyAggContext;

let pubkeys = [
    /* ... */
];

let key_agg_ctx = KeyAggContext::new(pubkeys)
    .unwrap()
    .with_xonly_tweak(
        "7931676703c0865d8b502dcdf1d956e86503796cfeabe33d12a918fbf408da05"
            .parse::<SecretKey>()
            .unwrap()
    )
    .unwrap();

let aggregated_pubkey: PublicKey = key_agg_ctx.aggregated_pubkey_untweaked();

assert_eq!(
    aggregated_pubkey,
    KeyAggContext::new(pubkeys).unwrap().aggregated_pubkey(),
)
source

pub fn tweak_sum<T: From<Scalar>>(&self) -> Option<T>

Returns the sum of all tweaks applied so far to this KeyAggContext. Returns None if the tweak sum is zero i.e. if no tweaks have been applied, or if the tweaks canceled each other out (by summing to zero).

source

pub fn pubkeys(&self) -> &[Point]

Returns a read-only reference to the ordered set of public keys which this KeyAggContext was created with.

source

pub fn pubkey_index(&self, pubkey: impl Into<Point>) -> Option<usize>

Looks up the index of a given pubkey in the key aggregation group. Returns None if the key is not a member of the group.

source

pub fn get_pubkey<T: From<Point>>(&self, index: usize) -> Option<T>

Returns the public key for a given signer’s index.

Keys are best identified by their index from zero, because MuSig allows more than one signer to share the same public key.

source

pub fn key_coefficient(&self, pubkey: impl Into<Point>) -> Option<MaybeScalar>

Finds the key coefficient for a given public key. Returns None if the given pubkey is not part of the aggregated key. This coefficient is the same for any two copies of the same public key.

Key coefficients are multiplicative tweaks applied to each public key in an aggregated MuSig key. They prevent rogue key attacks by ensuring that signers cannot effectively compute their public key as a function of the pubkeys of other signers.

The key coefficient is computed by hashing the public key X with a hash of the ordered set of all public keys in the signing group, denoted L. KeyAggContext caches these coefficients on instantiation.

source

pub fn effective_pubkey<T: From<MaybePoint>>( &self, pubkey: impl Into<Point> ) -> Option<T>

Finds the effective pubkey for a given individual pubkey. This is essentially the same as pubkey * key_agg_ctx.key_coefficient(pubkey), except it is faster than recomputing it manually because the key_agg_ctx caches this value internally.

Returns None if the given pubkey is not part of the aggregated key.

source

pub fn aggregated_seckey<T: From<Scalar>>( &self, seckeys: impl IntoIterator<Item = Scalar> ) -> Result<T, InvalidSecretKeysError>

Compute the aggregated secret key for the KeyAggContext given an ordered set of secret keys. Returns InvalidSecretKeysError if the secret keys do not align with the ordered set of pubkeys intially given to the KeyAggContext, which can be checked via the KeyAggContext::pubkeys method.

source§

impl KeyAggContext

source

pub fn serialize(&self) -> Vec<u8>

source

pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError<Self>>

source

pub fn from_hex(hex: &str) -> Result<Self, DecodeError<Self>>

Parses this type from a hex string, which can be either upper or lower case. The binary format of the decoded hex data should match that returned by to_bytes.

Same as Self::from_str.

Trait Implementations§

source§

impl BinaryEncoding for KeyAggContext

source§

fn to_bytes(&self) -> Self::Serialized

Serializes a key aggregation context object into binary format.

This is a variable-length encoding of the following fields:

  • header_byte (1 byte)
    • Lowest order bit is set if the parity of the aggregated pubkey should be negated upon deserialization (due to use of “x-only” tweaks).
    • Second lowest order bit is set if there is an accumulated tweak value present in the serialization.
  • tweak_acc [optional] (32 bytes)
    • A non-zero scalar representing the accumulated value of prior tweaks.
    • Present only if header_byte & 0b10 != 0.
  • n_pubkey (4 bytes)
    • Big-endian encoded u32, describing the number of pubkeys which are to follow.
  • ordered_pubkeys (33 * n_pubkey bytes)
    • The public keys needed to reconstruct the KeyAggContext, in the same order in which they were originally presented.

This is a custom data format, not drawn from any standards. An identical KeyAggContext can be reconstructed from this binary representation using KeyAggContext::from_bytes.

This is also the serialization implemented for serde::Serialize and serde::Deserialize if the serde feature of this crate is enabled.

source§

fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError<Self>>

Deserializes a KeyAggContext from its binary serialization. See KeyAggContext::to_bytes for a description of the expected binary format.

§

type Serialized = Vec<u8>

The binary type which is returned by serialization. Should either be [u8; N] or Vec<u8>.
source§

impl Clone for KeyAggContext

source§

fn clone(&self) -> KeyAggContext

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for KeyAggContext

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for KeyAggContext

source§

fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>

Deserializes this type from a byte vector or a hex string, depending on the human-readability of the data format.

source§

impl Display for KeyAggContext

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats this type as a lower-case hex string.

source§

impl From<KeyAggContext> for Vec<u8>

source§

fn from(value: KeyAggContext) -> Self

Serialize this type to a heap-allocated byte vector.

source§

impl FromStr for KeyAggContext

source§

fn from_str(hex: &str) -> Result<Self, Self::Err>

Parses this type from a hex string, which can be either upper or lower case. The binary format of the decoded hex data should match that returned by to_bytes.

Same as Self::from_hex.

§

type Err = DecodeError<KeyAggContext>

The associated error which can be returned from parsing.
source§

impl LowerHex for KeyAggContext

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter.
source§

impl PartialEq for KeyAggContext

source§

fn eq(&self, other: &Self) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl Serialize for KeyAggContext

source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Serialize this value into the given Serde serializer. Read more
source§

impl TryFrom<&[u8]> for KeyAggContext

source§

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error>

Parse this type from a variable-length byte slice.

Same as Self::from_bytes.

§

type Error = DecodeError<KeyAggContext>

The type returned in the event of a conversion error.
source§

impl UpperHex for KeyAggContext

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter.
source§

impl Eq for KeyAggContext

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

source§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,