dubp_documents/identity/
v10.rs

1//  Copyright (C) 2020  Éloïs SANCHEZ.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Wrappers around Identity documents V10.
17
18use crate::*;
19
20/// Wrap an Identity document.
21///
22/// Must be created by parsing a text document or using a builder.
23#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
24pub struct IdentityDocumentV10 {
25    /// Document as text.
26    ///
27    /// Is used to check signatures, and other values
28    /// must be extracted from it.
29    text: Option<String>,
30
31    /// Currency.
32    currency: String,
33    /// Unique ID
34    username: String,
35    /// Blockstamp
36    blockstamp: Blockstamp,
37    /// Document issuer (there should be only one).
38    issuer: ed25519::PublicKey,
39    /// Document signature (there should be only one).
40    signature: ed25519::Signature,
41}
42
43#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
44/// identity document for jsonification
45pub struct IdentityDocumentV10Stringified {
46    /// Currency.
47    pub currency: String,
48    /// Unique ID
49    pub username: String,
50    /// Blockstamp
51    pub blockstamp: String,
52    /// Document issuer
53    pub issuer: String,
54    /// Document signature
55    pub signature: String,
56}
57
58impl ToStringObject for IdentityDocumentV10 {
59    type StringObject = IdentityDocumentV10Stringified;
60    /// Transforms an object into a json object
61    fn to_string_object(&self) -> IdentityDocumentV10Stringified {
62        IdentityDocumentV10Stringified {
63            currency: self.currency.clone(),
64            username: self.username.clone(),
65            blockstamp: format!("{}", self.blockstamp),
66            issuer: self.issuer.to_base58(),
67            signature: self.signature.to_base64(),
68        }
69    }
70}
71
72impl IdentityDocumentV10 {
73    /// Unique ID
74    pub fn username(&self) -> &str {
75        &self.username
76    }
77
78    /// Lightens the identity (for example to store it while minimizing the space required)
79    pub fn reduce(&mut self) {
80        self.text = None;
81    }
82}
83
84impl Document for IdentityDocumentV10 {
85    type PublicKey = ed25519::PublicKey;
86
87    fn as_bytes(&self) -> BeefCow<[u8]> {
88        BeefCow::borrowed(self.as_text().as_bytes())
89    }
90
91    fn currency(&self) -> &str {
92        &self.currency
93    }
94
95    fn blockstamp(&self) -> Blockstamp {
96        self.blockstamp
97    }
98
99    fn issuers(&self) -> SmallVec<[Self::PublicKey; 1]> {
100        svec![self.issuer]
101    }
102
103    fn signatures(&self) -> SmallVec<[<Self::PublicKey as PublicKey>::Signature; 1]> {
104        svec![self.signature]
105    }
106
107    fn version(&self) -> usize {
108        10
109    }
110}
111
112/// CompactIdentityDocumentV10
113#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
114pub struct CompactIdentityDocumentV10 {
115    /// Unique ID
116    username: String,
117    /// Blockstamp
118    blockstamp: Blockstamp,
119    /// Document issuer
120    pubkey: ed25519::PublicKey,
121    /// Document signature
122    signature: ed25519::Signature,
123}
124
125impl CompactTextDocument for CompactIdentityDocumentV10 {
126    fn as_compact_text(&self) -> String {
127        format!(
128            "{issuer}:{signature}:{blockstamp}:{username}",
129            issuer = self.pubkey,
130            signature = self.signature,
131            blockstamp = self.blockstamp,
132            username = self.username,
133        )
134    }
135}
136
137impl TextDocument for IdentityDocumentV10 {
138    type CompactTextDocument_ = CompactIdentityDocumentV10;
139
140    fn as_text(&self) -> &str {
141        if let Some(ref text) = self.text {
142            text
143        } else {
144            panic!("Try to get text of reduce identity !")
145        }
146    }
147
148    fn to_compact_document(&self) -> Cow<Self::CompactTextDocument_> {
149        Cow::Owned(CompactIdentityDocumentV10 {
150            username: self.username.clone(),
151            blockstamp: self.blockstamp,
152            pubkey: self.issuer,
153            signature: self.signature,
154        })
155    }
156}
157
158/// Identity document builder.
159#[derive(Debug, Copy, Clone)]
160pub struct IdentityDocumentV10Builder<'a> {
161    /// Document currency.
162    pub currency: &'a str,
163    /// Identity unique id.
164    pub username: &'a str,
165    /// Reference blockstamp.
166    pub blockstamp: Blockstamp,
167    /// Document/identity issuer.
168    pub issuer: ed25519::PublicKey,
169}
170
171impl<'a> TextDocumentBuilder for IdentityDocumentV10Builder<'a> {
172    type Document = IdentityDocumentV10;
173    type Signator = ed25519::Signator;
174
175    fn build_with_text_and_sigs(
176        self,
177        text: String,
178        signatures: SmallVec<
179            [<<Self::Document as Document>::PublicKey as PublicKey>::Signature; 1],
180        >,
181    ) -> IdentityDocumentV10 {
182        IdentityDocumentV10 {
183            text: Some(text),
184            currency: self.currency.to_string(),
185            username: self.username.to_string(),
186            blockstamp: self.blockstamp,
187            issuer: self.issuer,
188            signature: signatures[0],
189        }
190    }
191
192    fn generate_text(&self) -> String {
193        format!(
194            "Version: 10
195Type: Identity
196Currency: {currency}
197Issuer: {issuer}
198UniqueID: {username}
199Timestamp: {blockstamp}
200",
201            currency = self.currency,
202            issuer = self.issuer,
203            username = self.username,
204            blockstamp = self.blockstamp
205        )
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212    use smallvec::smallvec;
213    use std::str::FromStr;
214    use unwrap::unwrap;
215
216    #[test]
217    fn generate_real_document() {
218        let keypair = ed25519::KeyPairFromSeed32Generator::generate(unwrap!(
219            Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
220            "fail to build Seed32"
221        ));
222        let pubkey = keypair.public_key();
223        let signator = keypair.generate_signator();
224
225        let sig = unwrap!(ed25519::Signature::from_base64(
226                "mmFepRsiOjILKnCvEvN3IZScLOfg8+e0JPAl5VkiuTLZRGJKgKhPy8nQlCKbeg0jefQm/2HJ78e/Sj+NMqYLCw==",
227            ), "fail to build Signature");
228
229        let blockstamp = unwrap!(
230            Blockstamp::from_str(
231                "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
232            ),
233            "fail to build Blockstamp"
234        );
235
236        let builder = IdentityDocumentV10Builder {
237            currency: "duniter_unit_test_currency",
238            username: "tic",
239            blockstamp,
240            issuer: pubkey,
241        };
242
243        assert!(builder
244            .build_with_signature(smallvec![sig])
245            .verify_signatures()
246            .is_ok());
247        assert!(builder
248            .build_and_sign(vec![signator])
249            .verify_signatures()
250            .is_ok());
251    }
252}