duniter_documents/blockchain/
mod.rs1use std::fmt::Debug;
19
20use duniter_crypto::keys::{PrivateKey, PublicKey};
21
22pub mod v10;
23
24#[derive(Debug, Clone)]
26pub enum BlockchainProtocol {
27 V10(Box<v10::documents::V10Document>),
29 V11(),
31}
32
33pub trait Document: Debug + Clone {
40 type PublicKey: PublicKey;
42 type CurrencyType: ?Sized;
44
45 fn version(&self) -> u16;
47
48 fn currency(&self) -> &Self::CurrencyType;
50
51 fn issuers(&self) -> &Vec<Self::PublicKey>;
53
54 fn signatures(&self) -> &Vec<<Self::PublicKey as PublicKey>::Signature>;
56
57 fn as_bytes(&self) -> &[u8];
59
60 fn verify_signatures(&self) -> VerificationResult {
62 let issuers_count = self.issuers().len();
63 let signatures_count = self.signatures().len();
64
65 if issuers_count != signatures_count {
66 VerificationResult::IncompletePairs(issuers_count, signatures_count)
67 } else {
68 let issuers = self.issuers();
69 let signatures = self.signatures();
70 let mismatches: Vec<_> = issuers
71 .iter()
72 .zip(signatures)
73 .enumerate()
74 .filter(|&(_, (key, signature))| !key.verify(self.as_bytes(), signature))
75 .map(|(i, _)| i)
76 .collect();
77
78 if mismatches.is_empty() {
79 VerificationResult::Valid()
80 } else {
81 VerificationResult::Invalid(mismatches)
82 }
83 }
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum VerificationResult {
90 Valid(),
92 IncompletePairs(usize, usize),
95 Invalid(Vec<usize>),
98}
99
100pub trait IntoSpecializedDocument<P> {
106 fn into_specialized(self) -> P;
108}
109
110pub trait DocumentBuilder {
112 type Document: Document;
114
115 type PrivateKey: PrivateKey<
117 Signature = <<Self::Document as Document>::PublicKey as PublicKey>::Signature,
118 >;
119
120 fn build_with_signature(
122 &self,
123 signatures: Vec<<<Self::Document as Document>::PublicKey as PublicKey>::Signature>,
124 ) -> Self::Document;
125
126 fn build_and_sign(&self, private_keys: Vec<Self::PrivateKey>) -> Self::Document;
128}
129
130pub trait DocumentParser<S, D, E> {
134 fn parse(source: S) -> Result<D, E>;
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use duniter_crypto::keys::{Signature, ed25519};
142
143 #[derive(Debug, Clone)]
145 struct PlainTextDocument {
146 pub text: &'static str,
147 pub issuers: Vec<ed25519::PublicKey>,
148 pub signatures: Vec<ed25519::Signature>,
149 }
150
151 impl Document for PlainTextDocument {
152 type PublicKey = ed25519::PublicKey;
153 type CurrencyType = str;
154
155 fn version(&self) -> u16 {
156 unimplemented!()
157 }
158
159 fn currency(&self) -> &str {
160 unimplemented!()
161 }
162
163 fn issuers(&self) -> &Vec<ed25519::PublicKey> {
164 &self.issuers
165 }
166
167 fn signatures(&self) -> &Vec<ed25519::Signature> {
168 &self.signatures
169 }
170
171 fn as_bytes(&self) -> &[u8] {
172 self.text.as_bytes()
173 }
174 }
175
176 #[test]
177 fn verify_signatures() {
178 let text = "Version: 10
179Type: Identity
180Currency: duniter_unit_test_currency
181Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
182UniqueID: tic
183Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
184";
185
186 let issuer1 = ed25519::PublicKey::from_base58(
188 "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV",
189 ).unwrap();
190
191 let sig1 = ed25519::Signature::from_base64(
192 "1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\
193 mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
194 ).unwrap();
195
196 let issuer2 = ed25519::PublicKey::from_base58(
198 "DNann1Lh55eZMEDXeYt32bzHbA3NJR46DeQYCS2qQdLV",
199 ).unwrap();
200
201 let sig2 = ed25519::Signature::from_base64(
202 "1eubHHbuNfilHHH0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\
203 mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
204 ).unwrap();
205
206 {
207 let doc = PlainTextDocument {
208 text,
209 issuers: vec![issuer1],
210 signatures: vec![sig1],
211 };
212
213 assert_eq!(doc.verify_signatures(), VerificationResult::Valid());
214 }
215
216 {
217 let doc = PlainTextDocument {
218 text,
219 issuers: vec![issuer1],
220 signatures: vec![sig2],
221 };
222
223 assert_eq!(
224 doc.verify_signatures(),
225 VerificationResult::Invalid(vec![0])
226 );
227 }
228
229 {
230 let doc = PlainTextDocument {
231 text,
232 issuers: vec![issuer1, issuer2],
233 signatures: vec![sig1],
234 };
235
236 assert_eq!(
237 doc.verify_signatures(),
238 VerificationResult::IncompletePairs(2, 1)
239 );
240 }
241
242 {
243 let doc = PlainTextDocument {
244 text,
245 issuers: vec![issuer1],
246 signatures: vec![sig1, sig2],
247 };
248
249 assert_eq!(
250 doc.verify_signatures(),
251 VerificationResult::IncompletePairs(1, 2)
252 );
253 }
254 }
255}