1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! MuSig Key
use crate::{
errors::{self, MuSigError},
MuSigContext, PublicKey, SchnorrError,
};
use bacteria::Transcript;
use mohan::dalek::{ristretto::RistrettoPoint, scalar::Scalar};
/// MuSig aggregated key context
#[derive(Clone)]
pub struct MultiKey {
prf: Option<Transcript>,
aggregated_key: PublicKey,
public_keys: Vec<PublicKey>,
}
impl MultiKey {
/// Constructs a new MuSig multikey aggregating the pubkeys.
pub fn new(pubkeys: Vec<PublicKey>) -> Result<Self, SchnorrError> {
match pubkeys.len() {
0 => {
return Err(errors::from_musig(MuSigError::BadArguments));
}
1 => {
// Special case: single key can be wrapped in a Multikey type
// without a delinearization factor applied.
return Ok(MultiKey {
prf: None,
aggregated_key: pubkeys[0],
public_keys: pubkeys,
});
}
_ => {}
}
// Create transcript for Multikey
let mut prf = Transcript::new(b"Musig.aggregated-key");
prf.append_u64(b"n", pubkeys.len() as u64);
// Commit pubkeys into the transcript
// <L> = H(X_1 || X_2 || ... || X_n)
for X in &pubkeys {
prf.commit_point(b"X", X.as_compressed());
}
// aggregated_key = sum_i ( a_i * X_i )
let mut aggregated_key = RistrettoPoint::default();
for (i, X) in pubkeys.iter().enumerate() {
let a = MultiKey::compute_factor(&prf, i);
let X = X.into_point();
aggregated_key = aggregated_key + a * X;
}
Ok(MultiKey {
prf: Some(prf),
aggregated_key: PublicKey::from_point(aggregated_key),
public_keys: pubkeys,
})
}
/// Returns `a_i` factor for component key in aggregated key.
/// a_i = H(<L>, X_i). The list of pubkeys, <L>, has already been committed to the transcript.
fn compute_factor(prf: &Transcript, i: usize) -> Scalar {
let mut a_i_prf = prf.clone();
a_i_prf.append_u64(b"i", i as u64);
a_i_prf.challenge_scalar(b"a_i")
}
/// Returns VerificationKey representation of aggregated key.
pub fn aggregated_key(&self) -> PublicKey {
self.aggregated_key
}
}
impl MuSigContext for MultiKey {
fn commit(&self, transcript: &mut Transcript) {
// Domain seperation
transcript.proto_name(b"organism_schnorr");
//commit corresponding public key
transcript.commit_point(b"public_key", self.aggregated_key.as_compressed());
}
fn challenge(&self, i: usize, transcript: &mut Transcript) -> Scalar {
// Make c = H(X, R, m)
// The message `m`, nonce commitment `R`, and aggregated key `X`
// have already been fed into the transcript.
let c = transcript.challenge_scalar(b"c");
// Make a_i, the per-party factor. a_i = H(<L>, X_i).
// The list of pubkeys, <L>, has already been committed to self.transcript.
let a_i = match &self.prf {
Some(t) => MultiKey::compute_factor(&t, i),
None => Scalar::one(),
};
c * a_i
}
fn len(&self) -> usize {
self.public_keys.len()
}
fn key(&self, index: usize) -> PublicKey {
self.public_keys[index]
}
}