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]
    }
}