1#![allow(clippy::upper_case_acronyms)]
19#![deny(
20 clippy::expect_used,
21 clippy::unwrap_used,
22 missing_debug_implementations,
23 missing_copy_implementations,
24 trivial_casts,
25 trivial_numeric_casts,
26 unsafe_code,
27 unstable_features,
28 unused_import_braces
29)]
30
31pub mod certification;
32pub mod identity;
33pub mod membership;
34pub mod revocation;
35mod traits;
36pub mod transaction;
37
38pub use dubp_wallet;
40pub use dubp_wallet::dubp_common;
41pub use dubp_wallet::smallvec;
42
43pub mod prelude {
45 pub use crate::traits::{
46 text::{CompactTextDocument, TextDocument, TextDocumentBuilder, TextDocumentFormat},
47 Document, DocumentBuilder, ToJsonObject, ToStringObject,
48 };
49 pub use crate::{DubpDocument, DubpDocumentStr};
50}
51
52pub(crate) use crate::prelude::*;
54pub(crate) use crate::transaction::{
55 TransactionDocumentTrait, TransactionSignErr, UTXOConditions, UnsignedTransactionDocumentTrait,
56};
57pub(crate) use beef::lean::Cow as BeefCow;
58pub(crate) use dubp_common::crypto::bases::b58::ToBase58;
59pub(crate) use dubp_common::crypto::hashs::Hash;
60pub(crate) use dubp_common::crypto::keys::*;
61pub(crate) use dubp_common::prelude::*;
62pub(crate) use dubp_wallet::prelude::*;
63pub(crate) use serde::{Deserialize, Serialize};
64pub(crate) use smallvec::{smallvec as svec, SmallVec, ToSmallVec};
65pub(crate) use std::{
66 borrow::Cow,
67 collections::{BTreeMap, BTreeSet, HashMap},
68 fmt::Debug,
69};
70
71#[derive(Debug)]
73pub enum SignedOrUnsignedDocument<S, U> {
74 Signed(S),
75 Unsigned(U),
76}
77
78#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
80pub enum DubpDocument {
81 Transaction(transaction::TransactionDocument),
83
84 Identity(identity::IdentityDocument),
86
87 Membership(membership::MembershipDocument),
89
90 Certification(certification::CertificationDocument),
92
93 Revocation(revocation::RevocationDocument),
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub enum DubpDocumentStr {
100 Transaction(Box<transaction::TransactionDocumentStringified>),
102
103 Identity(identity::IdentityDocumentStringified),
105
106 Membership(membership::MembershipDocumentStringified),
108
109 Certification(Box<certification::CertificationDocumentStringified>),
111
112 Revocation(Box<revocation::RevocationDocumentStringified>),
114}
115
116impl ToStringObject for DubpDocument {
117 type StringObject = DubpDocumentStr;
118
119 fn to_string_object(&self) -> Self::StringObject {
120 match *self {
121 DubpDocument::Identity(ref doc) => DubpDocumentStr::Identity(doc.to_string_object()),
122 DubpDocument::Membership(ref doc) => {
123 DubpDocumentStr::Membership(doc.to_string_object())
124 }
125 DubpDocument::Certification(ref doc) => {
126 DubpDocumentStr::Certification(Box::new(doc.to_string_object()))
127 }
128 DubpDocument::Revocation(ref doc) => {
129 DubpDocumentStr::Revocation(Box::new(doc.to_string_object()))
130 }
131 DubpDocument::Transaction(ref doc) => {
132 DubpDocumentStr::Transaction(Box::new(doc.to_string_object()))
133 }
134 }
135 }
136}
137
138macro_rules! dubp_document_fn {
139 ($fn_name:ident, $return_type:ty) => {
140 fn $fn_name(&self) -> $return_type {
141 match self {
142 Self::Certification(doc) => doc.$fn_name(),
143 Self::Identity(doc) => doc.$fn_name(),
144 Self::Membership(doc) => doc.$fn_name(),
145 Self::Revocation(doc) => doc.$fn_name(),
146 Self::Transaction(doc) => doc.$fn_name(),
147 }
148 }
149 };
150}
151
152impl Document for DubpDocument {
153 type PublicKey = PubKeyEnum;
154
155 dubp_document_fn!(as_bytes, BeefCow<[u8]>);
156 dubp_document_fn!(blockstamp, Blockstamp);
157 dubp_document_fn!(currency, &str);
158 dubp_document_fn!(issuers, SmallVec<[Self::PublicKey; 1]>);
159 dubp_document_fn!(
160 signatures,
161 SmallVec<[<Self::PublicKey as PublicKey>::Signature; 1]>
162 );
163 dubp_document_fn!(version, usize);
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use unwrap::unwrap;
171
172 #[derive(Debug, Clone, PartialEq, Eq)]
174 struct PlainTextDocument {
175 pub text: &'static str,
176 pub issuers: SmallVec<[PubKeyEnum; 1]>,
177 pub signatures: SmallVec<[Sig; 1]>,
178 }
179
180 impl Document for PlainTextDocument {
181 type PublicKey = PubKeyEnum;
182
183 fn version(&self) -> usize {
184 unimplemented!()
185 }
186
187 fn currency(&self) -> &str {
188 unimplemented!()
189 }
190
191 fn blockstamp(&self) -> Blockstamp {
192 unimplemented!()
193 }
194
195 fn issuers(&self) -> SmallVec<[Self::PublicKey; 1]> {
196 self.issuers.iter().copied().collect()
197 }
198
199 fn signatures(&self) -> SmallVec<[<Self::PublicKey as PublicKey>::Signature; 1]> {
200 self.signatures.iter().copied().collect()
201 }
202
203 fn as_bytes(&self) -> BeefCow<[u8]> {
204 BeefCow::borrowed(self.text.as_bytes())
205 }
206 }
207
208 #[test]
209 fn verify_signatures() {
210 let text = "Version: 10
211Type: Identity
212Currency: duniter_unit_test_currency
213Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
214UniqueID: tic
215Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
216";
217
218 let issuer1 = PubKeyEnum::Ed25519(unwrap!(
220 ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
221 "Fail to parse PublicKey from base58"
222 ));
223
224 let sig1 = Sig::Ed25519(unwrap!(
225 ed25519::Signature::from_base64(
226 "1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\
227 mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
228 ),
229 "Fail to parse Signature from base64"
230 ));
231
232 let issuer2 = PubKeyEnum::Ed25519(unwrap!(
234 ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt32bzHbA3NJR46DeQYCS2qQdLV"),
235 "Fail to parse PublicKey from base58"
236 ));
237
238 let sig2 = Sig::Ed25519(unwrap!(
239 ed25519::Signature::from_base64(
240 "1eubHHbuNfilHHH0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\
241 mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
242 ),
243 "Fail to parse Signature from base64"
244 ));
245
246 {
247 let doc = PlainTextDocument {
248 text,
249 issuers: svec![issuer1],
250 signatures: svec![sig1],
251 };
252
253 if let Err(e) = doc.verify_signatures() {
254 panic!("DocumentSigsErr: {:?}", e)
255 }
256 }
257
258 {
259 let doc = PlainTextDocument {
260 text,
261 issuers: svec![issuer1],
262 signatures: svec![sig2],
263 };
264 assert_eq!(
265 doc.verify_signatures(),
266 Err(DocumentSigsErr::Invalid(
267 maplit::hashmap![0 => SigError::InvalidSig]
268 ))
269 );
270 }
271
272 {
273 let doc = PlainTextDocument {
274 text,
275 issuers: svec![issuer1, issuer2],
276 signatures: svec![sig1],
277 };
278
279 assert_eq!(
280 doc.verify_signatures(),
281 Err(DocumentSigsErr::IncompletePairs(2, 1))
282 );
283 }
284
285 {
286 let doc = PlainTextDocument {
287 text,
288 issuers: svec![issuer1],
289 signatures: svec![sig1, sig2],
290 };
291
292 assert_eq!(
293 doc.verify_signatures(),
294 Err(DocumentSigsErr::IncompletePairs(1, 2))
295 );
296 }
297 }
298}