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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::{account, key};
use chain_crypto::{PublicKey, Signature};

use super::index::{Index, TreeIndex, LEVEL_MAXLIMIT};
pub use crate::transaction::WitnessMultisigData;
use thiserror::Error;

/// Account Identifier (also used as Public Key)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
    any(test, feature = "property-test-api"),
    derive(test_strategy::Arbitrary)
)]
pub struct Identifier(pub(crate) key::Hash);

impl AsRef<[u8]> for Identifier {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl From<[u8; 32]> for Identifier {
    fn from(a: [u8; 32]) -> Self {
        Identifier(a.into())
    }
}

impl From<Identifier> for [u8; 32] {
    fn from(a: Identifier) -> Self {
        a.0.into()
    }
}

#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum DeclarationError {
    #[error("Invalid threshold")]
    ThresholdInvalid,
    #[error("Not enough owners")]
    HasNotEnoughOwners,
    #[error("Too many owners")]
    HasTooManyOwners,
    #[error("Sub not implemented")]
    SubNotImplemented,
}

impl std::fmt::Display for Identifier {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

/// Declaration of a multisig account parameters which is:
///
/// * a threshold that need to be between 1 and the size of owners
/// * a bunch of owners which is either a hash of a key, or a sub declaration
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Declaration {
    pub(crate) threshold: u8, // between 1 and len(owners)
    pub(crate) owners: Vec<DeclElement>,
}

impl Declaration {
    pub fn threshold(&self) -> usize {
        self.threshold as usize
    }

    pub fn total(&self) -> usize {
        self.owners.len()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeclElement {
    Sub(Declaration),
    Owner(key::Hash),
}

impl DeclElement {
    pub fn to_hash(&self) -> key::Hash {
        match self {
            DeclElement::Sub(d) => d.to_identifier().0,
            DeclElement::Owner(hash) => *hash,
        }
    }

    pub fn from_publickey(key: &PublicKey<account::AccountAlg>) -> Self {
        DeclElement::Owner(key::Hash::hash_bytes(key.as_ref()))
    }
}

// Create an identifier by concatenating the threshold (as a byte) and all the owners
// and returning the hash of this content
pub(super) fn owners_to_identifier(threshold: u8, owners: &[DeclElement]) -> Identifier {
    let mut out = Vec::new();
    out.extend_from_slice(&[threshold]);
    for o in owners {
        out.extend_from_slice(o.to_hash().as_ref())
    }
    Identifier(key::Hash::hash_bytes(&out))
}

impl Declaration {
    /// Get the identifier associated with a declaration
    pub fn to_identifier(&self) -> Identifier {
        owners_to_identifier(self.threshold, &self.owners)
    }

    pub fn is_valid(&self) -> Result<(), DeclarationError> {
        if self.threshold < 1 || self.threshold as usize > self.owners.len() {
            return Err(DeclarationError::ThresholdInvalid);
        }
        if self.owners.len() <= 1 {
            return Err(DeclarationError::HasNotEnoughOwners);
        }
        if self.owners.len() > LEVEL_MAXLIMIT {
            return Err(DeclarationError::HasTooManyOwners);
        }
        Ok(())
    }

    pub fn get_path(&self, ti: TreeIndex) -> Option<(&Declaration, Index)> {
        match ti {
            TreeIndex::D1(idx) => Some((self, idx)),
            TreeIndex::D2(r, idx) if r.to_usize() < self.owners.len() => {
                match self.owners[r.to_usize()] {
                    DeclElement::Owner(_) => None,
                    DeclElement::Sub(ref d) => Some((d, idx)),
                }
            }
            TreeIndex::D2(_, _) => None,
        }
    }
}

pub type Pk = PublicKey<account::AccountAlg>;
pub type Sig = Signature<WitnessMultisigData, account::AccountAlg>;