1#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(missing_docs)]
20
21extern crate alloc;
24
25use alloc::vec::Vec;
26use codec::{Decode, DecodeWithMemTracking, Encode};
27use scale_info::{build::Fields, Path, Type, TypeInfo};
28use sp_application_crypto::RuntimeAppPublic;
29#[cfg(feature = "std")]
30use sp_core::Pair;
31
32pub type Topic = [u8; 32];
34pub type DecryptionKey = [u8; 32];
36pub type Hash = [u8; 32];
38pub type BlockHash = [u8; 32];
40pub type AccountId = [u8; 32];
42pub type Channel = [u8; 32];
44
45pub const MAX_TOPICS: usize = 4;
47
48#[cfg(feature = "std")]
49pub use store_api::{
50 Error, FilterDecision, InvalidReason, RejectionReason, Result, StatementSource, StatementStore,
51 SubmitResult,
52};
53
54#[cfg(feature = "std")]
55mod ecies;
56pub mod runtime_api;
57#[cfg(feature = "std")]
58mod store_api;
59
60mod sr25519 {
61 mod app_sr25519 {
62 use sp_application_crypto::{app_crypto, key_types::STATEMENT, sr25519};
63 app_crypto!(sr25519, STATEMENT);
64 }
65 pub type Public = app_sr25519::Public;
66}
67
68pub mod ed25519 {
70 mod app_ed25519 {
71 use sp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT};
72 app_crypto!(ed25519, STATEMENT);
73 }
74 pub type Public = app_ed25519::Public;
76 #[cfg(feature = "std")]
78 pub type Pair = app_ed25519::Pair;
79}
80
81mod ecdsa {
82 mod app_ecdsa {
83 use sp_application_crypto::{app_crypto, ecdsa, key_types::STATEMENT};
84 app_crypto!(ecdsa, STATEMENT);
85 }
86 pub type Public = app_ecdsa::Public;
87}
88
89#[cfg(feature = "std")]
91pub fn hash_encoded(data: &[u8]) -> [u8; 32] {
92 sp_crypto_hashing::blake2_256(data)
93}
94
95#[derive(Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug, Clone, PartialEq, Eq)]
97pub enum Proof {
98 Sr25519 {
100 signature: [u8; 64],
102 signer: [u8; 32],
104 },
105 Ed25519 {
107 signature: [u8; 64],
109 signer: [u8; 32],
111 },
112 Secp256k1Ecdsa {
114 signature: [u8; 65],
116 signer: [u8; 33],
118 },
119 OnChain {
121 who: AccountId,
123 block_hash: BlockHash,
125 event_index: u64,
127 },
128}
129
130impl Proof {
131 pub fn account_id(&self) -> AccountId {
133 match self {
134 Proof::Sr25519 { signer, .. } => *signer,
135 Proof::Ed25519 { signer, .. } => *signer,
136 Proof::Secp256k1Ecdsa { signer, .. } =>
137 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer).into(),
138 Proof::OnChain { who, .. } => *who,
139 }
140 }
141}
142
143#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
146#[repr(u8)]
147pub enum Field {
148 AuthenticityProof(Proof) = 0,
150 DecryptionKey(DecryptionKey) = 1,
152 Priority(u32) = 2,
154 Channel(Channel) = 3,
156 Topic1(Topic) = 4,
158 Topic2(Topic) = 5,
160 Topic3(Topic) = 6,
162 Topic4(Topic) = 7,
164 Data(Vec<u8>) = 8,
166}
167
168impl Field {
169 fn discriminant(&self) -> u8 {
170 unsafe { *(self as *const Self as *const u8) }
173 }
174}
175
176#[derive(DecodeWithMemTracking, Debug, Clone, PartialEq, Eq, Default)]
178pub struct Statement {
179 proof: Option<Proof>,
180 decryption_key: Option<DecryptionKey>,
181 channel: Option<Channel>,
182 priority: Option<u32>,
183 num_topics: u8,
184 topics: [Topic; MAX_TOPICS],
185 data: Option<Vec<u8>>,
186}
187
188impl TypeInfo for Statement {
191 type Identity = Self;
192
193 fn type_info() -> Type {
194 Type::builder()
196 .path(Path::new("Statement", module_path!()))
197 .docs(&["Statement structure"])
198 .composite(Fields::unnamed().field(|f| f.ty::<Vec<Field>>()))
199 }
200}
201
202impl Decode for Statement {
203 fn decode<I: codec::Input>(input: &mut I) -> core::result::Result<Self, codec::Error> {
204 let num_fields: codec::Compact<u32> = Decode::decode(input)?;
207 let mut tag = 0;
208 let mut statement = Statement::new();
209 for i in 0..num_fields.into() {
210 let field: Field = Decode::decode(input)?;
211 if i > 0 && field.discriminant() <= tag {
212 return Err("Invalid field order or duplicate fields".into())
213 }
214 tag = field.discriminant();
215 match field {
216 Field::AuthenticityProof(p) => statement.set_proof(p),
217 Field::DecryptionKey(key) => statement.set_decryption_key(key),
218 Field::Priority(p) => statement.set_priority(p),
219 Field::Channel(c) => statement.set_channel(c),
220 Field::Topic1(t) => statement.set_topic(0, t),
221 Field::Topic2(t) => statement.set_topic(1, t),
222 Field::Topic3(t) => statement.set_topic(2, t),
223 Field::Topic4(t) => statement.set_topic(3, t),
224 Field::Data(data) => statement.set_plain_data(data),
225 }
226 }
227 Ok(statement)
228 }
229}
230
231impl Encode for Statement {
232 fn encode(&self) -> Vec<u8> {
233 self.encoded(false)
234 }
235}
236
237#[derive(Clone, Copy, PartialEq, Eq, Debug)]
238pub enum SignatureVerificationResult {
240 Valid(AccountId),
242 Invalid,
244 NoSignature,
246}
247
248impl Statement {
249 pub fn new() -> Statement {
251 Default::default()
252 }
253
254 pub fn new_with_proof(proof: Proof) -> Statement {
256 let mut statement = Self::new();
257 statement.set_proof(proof);
258 statement
259 }
260
261 pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool {
267 let to_sign = self.signature_material();
268 if let Some(signature) = key.sign(&to_sign) {
269 let proof = Proof::Sr25519 {
270 signature: signature.into_inner().into(),
271 signer: key.clone().into_inner().into(),
272 };
273 self.set_proof(proof);
274 true
275 } else {
276 false
277 }
278 }
279
280 #[cfg(feature = "std")]
282 pub fn sign_sr25519_private(&mut self, key: &sp_core::sr25519::Pair) {
283 let to_sign = self.signature_material();
284 let proof =
285 Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
286 self.set_proof(proof);
287 }
288
289 pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool {
295 let to_sign = self.signature_material();
296 if let Some(signature) = key.sign(&to_sign) {
297 let proof = Proof::Ed25519 {
298 signature: signature.into_inner().into(),
299 signer: key.clone().into_inner().into(),
300 };
301 self.set_proof(proof);
302 true
303 } else {
304 false
305 }
306 }
307
308 #[cfg(feature = "std")]
310 pub fn sign_ed25519_private(&mut self, key: &sp_core::ed25519::Pair) {
311 let to_sign = self.signature_material();
312 let proof =
313 Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
314 self.set_proof(proof);
315 }
316
317 pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool {
327 let to_sign = self.signature_material();
328 if let Some(signature) = key.sign(&to_sign) {
329 let proof = Proof::Secp256k1Ecdsa {
330 signature: signature.into_inner().into(),
331 signer: key.clone().into_inner().0,
332 };
333 self.set_proof(proof);
334 true
335 } else {
336 false
337 }
338 }
339
340 #[cfg(feature = "std")]
342 pub fn sign_ecdsa_private(&mut self, key: &sp_core::ecdsa::Pair) {
343 let to_sign = self.signature_material();
344 let proof =
345 Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 };
346 self.set_proof(proof);
347 }
348
349 pub fn verify_signature(&self) -> SignatureVerificationResult {
351 use sp_runtime::traits::Verify;
352
353 match self.proof() {
354 Some(Proof::OnChain { .. }) | None => SignatureVerificationResult::NoSignature,
355 Some(Proof::Sr25519 { signature, signer }) => {
356 let to_sign = self.signature_material();
357 let signature = sp_core::sr25519::Signature::from(*signature);
358 let public = sp_core::sr25519::Public::from(*signer);
359 if signature.verify(to_sign.as_slice(), &public) {
360 SignatureVerificationResult::Valid(*signer)
361 } else {
362 SignatureVerificationResult::Invalid
363 }
364 },
365 Some(Proof::Ed25519 { signature, signer }) => {
366 let to_sign = self.signature_material();
367 let signature = sp_core::ed25519::Signature::from(*signature);
368 let public = sp_core::ed25519::Public::from(*signer);
369 if signature.verify(to_sign.as_slice(), &public) {
370 SignatureVerificationResult::Valid(*signer)
371 } else {
372 SignatureVerificationResult::Invalid
373 }
374 },
375 Some(Proof::Secp256k1Ecdsa { signature, signer }) => {
376 let to_sign = self.signature_material();
377 let signature = sp_core::ecdsa::Signature::from(*signature);
378 let public = sp_core::ecdsa::Public::from(*signer);
379 if signature.verify(to_sign.as_slice(), &public) {
380 let sender_hash =
381 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer);
382 SignatureVerificationResult::Valid(sender_hash.into())
383 } else {
384 SignatureVerificationResult::Invalid
385 }
386 },
387 }
388 }
389
390 #[cfg(feature = "std")]
392 pub fn hash(&self) -> [u8; 32] {
393 self.using_encoded(hash_encoded)
394 }
395
396 pub fn topic(&self, index: usize) -> Option<Topic> {
398 if index < self.num_topics as usize {
399 Some(self.topics[index])
400 } else {
401 None
402 }
403 }
404
405 pub fn decryption_key(&self) -> Option<DecryptionKey> {
407 self.decryption_key
408 }
409
410 pub fn into_data(self) -> Option<Vec<u8>> {
412 self.data
413 }
414
415 pub fn proof(&self) -> Option<&Proof> {
417 self.proof.as_ref()
418 }
419
420 pub fn account_id(&self) -> Option<AccountId> {
422 self.proof.as_ref().map(Proof::account_id)
423 }
424
425 pub fn data(&self) -> Option<&Vec<u8>> {
427 self.data.as_ref()
428 }
429
430 pub fn data_len(&self) -> usize {
432 self.data().map_or(0, Vec::len)
433 }
434
435 pub fn channel(&self) -> Option<Channel> {
437 self.channel
438 }
439
440 pub fn priority(&self) -> Option<u32> {
442 self.priority
443 }
444
445 fn signature_material(&self) -> Vec<u8> {
447 self.encoded(true)
448 }
449
450 pub fn remove_proof(&mut self) {
452 self.proof = None;
453 }
454
455 pub fn set_proof(&mut self, proof: Proof) {
457 self.proof = Some(proof)
458 }
459
460 pub fn set_priority(&mut self, priority: u32) {
462 self.priority = Some(priority)
463 }
464
465 pub fn set_channel(&mut self, channel: Channel) {
467 self.channel = Some(channel)
468 }
469
470 pub fn set_topic(&mut self, index: usize, topic: Topic) {
472 if index < MAX_TOPICS {
473 self.topics[index] = topic;
474 self.num_topics = self.num_topics.max(index as u8 + 1);
475 }
476 }
477
478 pub fn set_decryption_key(&mut self, key: DecryptionKey) {
480 self.decryption_key = Some(key);
481 }
482
483 pub fn set_plain_data(&mut self, data: Vec<u8>) {
485 self.data = Some(data)
486 }
487
488 fn encoded(&self, for_signing: bool) -> Vec<u8> {
489 let num_fields = if !for_signing && self.proof.is_some() { 1 } else { 0 } +
492 if self.decryption_key.is_some() { 1 } else { 0 } +
493 if self.priority.is_some() { 1 } else { 0 } +
494 if self.channel.is_some() { 1 } else { 0 } +
495 if self.data.is_some() { 1 } else { 0 } +
496 self.num_topics as u32;
497
498 let mut output = Vec::new();
499 if !for_signing {
503 let compact_len = codec::Compact::<u32>(num_fields);
504 compact_len.encode_to(&mut output);
505
506 if let Some(proof) = &self.proof {
507 0u8.encode_to(&mut output);
508 proof.encode_to(&mut output);
509 }
510 }
511 if let Some(decryption_key) = &self.decryption_key {
512 1u8.encode_to(&mut output);
513 decryption_key.encode_to(&mut output);
514 }
515 if let Some(priority) = &self.priority {
516 2u8.encode_to(&mut output);
517 priority.encode_to(&mut output);
518 }
519 if let Some(channel) = &self.channel {
520 3u8.encode_to(&mut output);
521 channel.encode_to(&mut output);
522 }
523 for t in 0..self.num_topics {
524 (4u8 + t).encode_to(&mut output);
525 self.topics[t as usize].encode_to(&mut output);
526 }
527 if let Some(data) = &self.data {
528 8u8.encode_to(&mut output);
529 data.encode_to(&mut output);
530 }
531 output
532 }
533
534 #[cfg(feature = "std")]
536 pub fn encrypt(
537 &mut self,
538 data: &[u8],
539 key: &sp_core::ed25519::Public,
540 ) -> core::result::Result<(), ecies::Error> {
541 let encrypted = ecies::encrypt_ed25519(key, data)?;
542 self.data = Some(encrypted);
543 self.decryption_key = Some((*key).into());
544 Ok(())
545 }
546
547 #[cfg(feature = "std")]
549 pub fn decrypt_private(
550 &self,
551 key: &sp_core::ed25519::Pair,
552 ) -> core::result::Result<Option<Vec<u8>>, ecies::Error> {
553 self.data.as_ref().map(|d| ecies::decrypt_ed25519(key, d)).transpose()
554 }
555}
556
557#[cfg(test)]
558mod test {
559 use crate::{hash_encoded, Field, Proof, SignatureVerificationResult, Statement};
560 use codec::{Decode, Encode};
561 use scale_info::{MetaType, TypeInfo};
562 use sp_application_crypto::Pair;
563
564 #[test]
565 fn statement_encoding_matches_vec() {
566 let mut statement = Statement::new();
567 assert!(statement.proof().is_none());
568 let proof = Proof::OnChain { who: [42u8; 32], block_hash: [24u8; 32], event_index: 66 };
569
570 let decryption_key = [0xde; 32];
571 let topic1 = [0x01; 32];
572 let topic2 = [0x02; 32];
573 let data = vec![55, 99];
574 let priority = 999;
575 let channel = [0xcc; 32];
576
577 statement.set_proof(proof.clone());
578 statement.set_decryption_key(decryption_key);
579 statement.set_priority(priority);
580 statement.set_channel(channel);
581 statement.set_topic(0, topic1);
582 statement.set_topic(1, topic2);
583 statement.set_plain_data(data.clone());
584
585 statement.set_topic(5, [0x55; 32]);
586 assert_eq!(statement.topic(5), None);
587
588 let fields = vec![
589 Field::AuthenticityProof(proof.clone()),
590 Field::DecryptionKey(decryption_key),
591 Field::Priority(priority),
592 Field::Channel(channel),
593 Field::Topic1(topic1),
594 Field::Topic2(topic2),
595 Field::Data(data.clone()),
596 ];
597
598 let encoded = statement.encode();
599 assert_eq!(statement.hash(), hash_encoded(&encoded));
600 assert_eq!(encoded, fields.encode());
601
602 let decoded = Statement::decode(&mut encoded.as_slice()).unwrap();
603 assert_eq!(decoded, statement);
604 }
605
606 #[test]
607 fn decode_checks_fields() {
608 let topic1 = [0x01; 32];
609 let topic2 = [0x02; 32];
610 let priority = 999;
611
612 let fields = vec![
613 Field::Priority(priority),
614 Field::Topic1(topic1),
615 Field::Topic1(topic1),
616 Field::Topic2(topic2),
617 ]
618 .encode();
619
620 assert!(Statement::decode(&mut fields.as_slice()).is_err());
621
622 let fields =
623 vec![Field::Topic1(topic1), Field::Priority(priority), Field::Topic2(topic2)].encode();
624
625 assert!(Statement::decode(&mut fields.as_slice()).is_err());
626 }
627
628 #[test]
629 fn sign_and_verify() {
630 let mut statement = Statement::new();
631 statement.set_plain_data(vec![42]);
632
633 let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
634 let ed25519_kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap();
635 let secp256k1_kp = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
636
637 statement.sign_sr25519_private(&sr25519_kp);
638 assert_eq!(
639 statement.verify_signature(),
640 SignatureVerificationResult::Valid(sr25519_kp.public().0)
641 );
642
643 statement.sign_ed25519_private(&ed25519_kp);
644 assert_eq!(
645 statement.verify_signature(),
646 SignatureVerificationResult::Valid(ed25519_kp.public().0)
647 );
648
649 statement.sign_ecdsa_private(&secp256k1_kp);
650 assert_eq!(
651 statement.verify_signature(),
652 SignatureVerificationResult::Valid(sp_crypto_hashing::blake2_256(
653 &secp256k1_kp.public().0
654 ))
655 );
656
657 statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
659 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
660
661 statement.remove_proof();
662 assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature);
663 }
664
665 #[test]
666 fn encrypt_decrypt() {
667 let mut statement = Statement::new();
668 let (pair, _) = sp_core::ed25519::Pair::generate();
669 let plain = b"test data".to_vec();
670
671 statement.encrypt(&plain, &pair.public()).unwrap();
673 assert_ne!(plain.as_slice(), statement.data().unwrap().as_slice());
674
675 let decrypted = statement.decrypt_private(&pair).unwrap();
676 assert_eq!(decrypted, Some(plain));
677 }
678
679 #[test]
680 fn statement_type_info_matches_encoding() {
681 let statement_type = Statement::type_info();
684 let vec_field_meta = MetaType::new::<Vec<Field>>();
685
686 match statement_type.type_def {
688 scale_info::TypeDef::Composite(composite) => {
689 assert_eq!(composite.fields.len(), 1, "Statement should have exactly one field");
690 let field = &composite.fields[0];
691 assert!(field.name.is_none(), "Field should be unnamed (newtype pattern)");
692 assert_eq!(field.ty, vec_field_meta, "Statement's inner type should be Vec<Field>");
693 },
694 _ => panic!("Statement TypeInfo should be a Composite"),
695 }
696 }
697}