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
impl KeyAggContext
sourcepub fn new<I, P>(pubkeys: I) -> Result<Self, KeyAggError>
pub fn new<I, P>(pubkeys: I) -> Result<Self, KeyAggError>
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.
sourcepub fn with_tweak(
self,
tweak: impl Into<Scalar>,
is_xonly: bool
) -> Result<Self, TweakError>
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"
);
sourcepub fn with_tweaks<S, I>(self, tweaks: I) -> Result<Self, TweakError>
pub fn with_tweaks<S, I>(self, tweaks: I) -> Result<Self, TweakError>
Iteratively applies tweaks to the aggregated pubkey. See KeyAggContext::with_tweak
.
sourcepub fn with_plain_tweak(
self,
tweak: impl Into<Scalar>
) -> Result<Self, TweakError>
pub fn with_plain_tweak( self, tweak: impl Into<Scalar> ) -> Result<Self, TweakError>
Same as self.with_tweak(tweak, false)
. See KeyAggContext::with_tweak
.
sourcepub fn with_xonly_tweak(
self,
tweak: impl Into<Scalar>
) -> Result<Self, TweakError>
pub fn with_xonly_tweak( self, tweak: impl Into<Scalar> ) -> Result<Self, TweakError>
Same as self.with_tweak(tweak, true)
. See KeyAggContext::with_tweak
.
sourcepub fn with_taproot_tweak(
self,
merkle_root: &[u8; 32]
) -> Result<Self, TweakError>
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.
sourcepub fn with_unspendable_taproot_tweak(self) -> Result<Self, TweakError>
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.
sourcepub fn aggregated_pubkey<T: From<Point>>(&self) -> T
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.
sourcepub fn aggregated_pubkey_untweaked<T: From<Point>>(&self) -> T
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(),
)
sourcepub fn tweak_sum<T: From<Scalar>>(&self) -> Option<T>
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).
sourcepub fn pubkeys(&self) -> &[Point]
pub fn pubkeys(&self) -> &[Point]
Returns a read-only reference to the ordered set of public keys
which this KeyAggContext
was created with.
sourcepub fn pubkey_index(&self, pubkey: impl Into<Point>) -> Option<usize>
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.
sourcepub fn get_pubkey<T: From<Point>>(&self, index: usize) -> Option<T>
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.
sourcepub fn key_coefficient(&self, pubkey: impl Into<Point>) -> Option<MaybeScalar>
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.
sourcepub fn effective_pubkey<T: From<MaybePoint>>(
&self,
pubkey: impl Into<Point>
) -> Option<T>
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.
sourcepub fn aggregated_seckey<T: From<Scalar>>(
&self,
seckeys: impl IntoIterator<Item = Scalar>
) -> Result<T, InvalidSecretKeysError>
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
impl KeyAggContext
sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError<Self>>
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError<Self>>
sourcepub fn from_hex(hex: &str) -> Result<Self, DecodeError<Self>>
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
impl BinaryEncoding for KeyAggContext
source§fn to_bytes(&self) -> Self::Serialized
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.
- Big-endian encoded
ordered_pubkeys
(33 *n_pubkey
bytes)- The public keys needed to reconstruct the
KeyAggContext
, in the same order in which they were originally presented.
- The public keys needed to reconstruct the
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>>
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>
type Serialized = Vec<u8>
[u8; N]
or Vec<u8>
.source§impl Clone for KeyAggContext
impl Clone for KeyAggContext
source§fn clone(&self) -> KeyAggContext
fn clone(&self) -> KeyAggContext
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl Debug for KeyAggContext
impl Debug for KeyAggContext
source§impl<'de> Deserialize<'de> for KeyAggContext
impl<'de> Deserialize<'de> for KeyAggContext
source§fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
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
impl Display for KeyAggContext
source§impl From<KeyAggContext> for Vec<u8>
impl From<KeyAggContext> for Vec<u8>
source§fn from(value: KeyAggContext) -> Self
fn from(value: KeyAggContext) -> Self
Serialize this type to a heap-allocated byte vector.
source§impl FromStr for KeyAggContext
impl FromStr for KeyAggContext
source§fn from_str(hex: &str) -> Result<Self, Self::Err>
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>
type Err = DecodeError<KeyAggContext>
source§impl LowerHex for KeyAggContext
impl LowerHex for KeyAggContext
source§impl PartialEq for KeyAggContext
impl PartialEq for KeyAggContext
source§impl Serialize for KeyAggContext
impl Serialize for KeyAggContext
source§impl TryFrom<&[u8]> for KeyAggContext
impl TryFrom<&[u8]> for KeyAggContext
source§fn try_from(bytes: &[u8]) -> Result<Self, Self::Error>
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error>
Parse this type from a variable-length byte slice.
Same as Self::from_bytes
.