oasis_runtime_sdk/crypto/multisig/
mod.rs

1use thiserror::Error;
2
3use crate::crypto::signature::{PublicKey, Signature};
4
5#[cfg(test)]
6mod test;
7
8/// Error.
9#[derive(Error, Debug)]
10pub enum Error {
11    #[error("invalid config")]
12    InvalidConfig,
13    #[error("invalid signature set")]
14    InvalidSignatureSet,
15    #[error("insufficient weight")]
16    InsufficientWeight,
17}
18
19/// One of the signers in a multisig configuration.
20#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
21#[cbor(no_default)]
22pub struct Signer {
23    /// The public key of the signer.
24    pub public_key: PublicKey,
25    /// The weight of the signer.
26    pub weight: u64,
27}
28
29/// A set of signatures corresponding to a multisig configuration.
30/// The indices match the configuration's `signers` vec.
31pub type SignatureSet = [Option<Signature>];
32/// A `SignatureSet` owned in a `Vec`.
33pub type SignatureSetOwned = Vec<Option<Signature>>;
34
35/// A multisig configuration.
36/// A set of signers with total "weight" greater than or equal to a "threshold" can authenticate
37/// for the configuration.
38#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
39pub struct Config {
40    /// The signers.
41    pub signers: Vec<Signer>,
42    /// The threshold.
43    pub threshold: u64,
44}
45
46impl Config {
47    /// Performs some sanity checks. This looks at the configuration only. There is no cryptographic
48    /// verification of any signatures.
49    pub fn validate_basic(&self) -> Result<(), Error> {
50        if self.threshold == 0 {
51            return Err(Error::InvalidConfig);
52        }
53        let mut total: u64 = 0;
54        for (i, signer) in self.signers.iter().enumerate() {
55            if self
56                .signers
57                .iter()
58                .take(i)
59                .any(|other_signer| signer.public_key == other_signer.public_key)
60            {
61                return Err(Error::InvalidConfig);
62            }
63            if signer.weight == 0 {
64                return Err(Error::InvalidConfig);
65            }
66            total = total
67                .checked_add(signer.weight)
68                .ok_or(Error::InvalidConfig)?;
69        }
70        if total < self.threshold {
71            return Err(Error::InvalidConfig);
72        }
73        Ok(())
74    }
75
76    /// Checks that the configuration and signature set are acceptable.
77    /// Returns vectors of public keys and signatures for batch verification of included signatures.
78    pub fn batch(
79        &self,
80        signature_set: &SignatureSet,
81    ) -> Result<(Vec<PublicKey>, Vec<Signature>), Error> {
82        self.validate_basic()?;
83        if signature_set.len() != self.signers.len() {
84            return Err(Error::InvalidSignatureSet);
85        }
86        let mut total = 0;
87        let mut public_keys = vec![];
88        let mut signatures = vec![];
89        for (signer, signature_o) in self.signers.iter().zip(signature_set.iter()) {
90            if let Some(signature) = signature_o {
91                total += signer.weight;
92                public_keys.push(signer.public_key.clone());
93                signatures.push(signature.clone());
94            }
95        }
96        if total < self.threshold {
97            return Err(Error::InsufficientWeight);
98        }
99        Ok((public_keys, signatures))
100    }
101}