1#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(missing_docs)]
20
21extern crate alloc;
24
25use alloc::vec::Vec;
26use codec::{Compact, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
27use core::ops::Deref;
28use scale_info::{build::Fields, Path, Type, TypeInfo};
29use sp_application_crypto::RuntimeAppPublic;
30#[cfg(feature = "std")]
31use sp_core::Pair;
32
33#[derive(
37 Clone,
38 Copy,
39 Debug,
40 Default,
41 PartialEq,
42 Eq,
43 PartialOrd,
44 Ord,
45 Hash,
46 Encode,
47 Decode,
48 DecodeWithMemTracking,
49 MaxEncodedLen,
50 TypeInfo,
51)]
52pub struct Topic(pub [u8; 32]);
53
54#[cfg(feature = "serde")]
55impl serde::Serialize for Topic {
56 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
57 where
58 S: serde::Serializer,
59 {
60 sp_core::bytes::serialize(&self.0, serializer)
61 }
62}
63
64#[cfg(feature = "serde")]
65impl<'de> serde::Deserialize<'de> for Topic {
66 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
67 where
68 D: serde::Deserializer<'de>,
69 {
70 let mut arr = [0u8; 32];
71 sp_core::bytes::deserialize_check_len(
72 deserializer,
73 sp_core::bytes::ExpectedLen::Exact(&mut arr[..]),
74 )?;
75 Ok(Topic(arr))
76 }
77}
78
79impl From<[u8; 32]> for Topic {
80 fn from(inner: [u8; 32]) -> Self {
81 Topic(inner)
82 }
83}
84
85impl From<Topic> for [u8; 32] {
86 fn from(topic: Topic) -> Self {
87 topic.0
88 }
89}
90
91impl AsRef<[u8; 32]> for Topic {
92 fn as_ref(&self) -> &[u8; 32] {
93 &self.0
94 }
95}
96
97impl AsRef<[u8]> for Topic {
98 fn as_ref(&self) -> &[u8] {
99 &self.0
100 }
101}
102
103impl Deref for Topic {
104 type Target = [u8; 32];
105
106 fn deref(&self) -> &Self::Target {
107 &self.0
108 }
109}
110
111pub type DecryptionKey = [u8; 32];
113pub type Hash = [u8; 32];
115pub type BlockHash = [u8; 32];
117pub type AccountId = [u8; 32];
119pub type Channel = [u8; 32];
121
122pub const MAX_TOPICS: usize = 4;
124pub const MAX_ANY_TOPICS: usize = 128;
127
128#[derive(Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
130pub struct StatementAllowance {
131 pub max_count: u32,
133 pub max_size: u32,
135}
136
137impl StatementAllowance {
138 pub fn new(max_count: u32, max_size: u32) -> Self {
140 Self { max_count, max_size }
141 }
142
143 pub const fn saturating_add(self, rhs: StatementAllowance) -> StatementAllowance {
145 StatementAllowance {
146 max_count: self.max_count.saturating_add(rhs.max_count),
147 max_size: self.max_size.saturating_add(rhs.max_size),
148 }
149 }
150
151 pub const fn saturating_sub(self, rhs: StatementAllowance) -> StatementAllowance {
153 StatementAllowance {
154 max_count: self.max_count.saturating_sub(rhs.max_count),
155 max_size: self.max_size.saturating_sub(rhs.max_size),
156 }
157 }
158
159 pub fn is_depleted(&self) -> bool {
161 self.max_count == 0 || self.max_size == 0
162 }
163}
164
165pub const STATEMENT_ALLOWANCE_PREFIX: &[u8] = b":statement_allowance:";
167
168pub fn statement_allowance_key(account_id: impl AsRef<[u8]>) -> Vec<u8> {
176 let mut key = STATEMENT_ALLOWANCE_PREFIX.to_vec();
177 key.extend_from_slice(account_id.as_ref());
178 key
179}
180
181pub fn increase_allowance_by(account_id: impl AsRef<[u8]>, by: StatementAllowance) {
183 let key = statement_allowance_key(account_id);
184 let mut allowance: StatementAllowance = frame_support::storage::unhashed::get_or_default(&key);
185 allowance = allowance.saturating_add(by);
186 frame_support::storage::unhashed::put(&key, &allowance);
187}
188
189pub fn decrease_allowance_by(account_id: impl AsRef<[u8]>, by: StatementAllowance) {
191 let key = statement_allowance_key(account_id);
192 let mut allowance: StatementAllowance = frame_support::storage::unhashed::get_or_default(&key);
193 allowance = allowance.saturating_sub(by);
194 if allowance.is_depleted() {
195 frame_support::storage::unhashed::kill(&key);
196 } else {
197 frame_support::storage::unhashed::put(&key, &allowance);
198 }
199}
200
201pub fn get_allowance(account_id: impl AsRef<[u8]>) -> StatementAllowance {
203 let key = statement_allowance_key(account_id);
204 frame_support::storage::unhashed::get_or_default(&key)
205}
206
207#[cfg(feature = "std")]
208pub use store_api::{
209 Error, FilterDecision, InvalidReason, OptimizedTopicFilter, RejectionReason, Result,
210 StatementEvent, StatementSource, StatementStore, SubmitResult, TopicFilter,
211};
212
213#[cfg(feature = "std")]
214mod ecies;
215pub mod runtime_api;
216#[cfg(feature = "std")]
217mod store_api;
218
219mod sr25519 {
220 mod app_sr25519 {
221 use sp_application_crypto::{app_crypto, key_types::STATEMENT, sr25519};
222 app_crypto!(sr25519, STATEMENT);
223 }
224 pub type Public = app_sr25519::Public;
225}
226
227pub mod ed25519 {
229 mod app_ed25519 {
230 use sp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT};
231 app_crypto!(ed25519, STATEMENT);
232 }
233 pub type Public = app_ed25519::Public;
235 #[cfg(feature = "std")]
237 pub type Pair = app_ed25519::Pair;
238}
239
240mod ecdsa {
241 mod app_ecdsa {
242 use sp_application_crypto::{app_crypto, ecdsa, key_types::STATEMENT};
243 app_crypto!(ecdsa, STATEMENT);
244 }
245 pub type Public = app_ecdsa::Public;
246}
247
248#[cfg(feature = "std")]
250pub fn hash_encoded(data: &[u8]) -> [u8; 32] {
251 sp_crypto_hashing::blake2_256(data)
252}
253
254#[derive(
256 Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, Debug, Clone, PartialEq, Eq,
257)]
258pub enum Proof {
259 Sr25519 {
261 signature: [u8; 64],
263 signer: [u8; 32],
265 },
266 Ed25519 {
268 signature: [u8; 64],
270 signer: [u8; 32],
272 },
273 Secp256k1Ecdsa {
275 signature: [u8; 65],
277 signer: [u8; 33],
279 },
280 OnChain {
282 who: AccountId,
284 block_hash: BlockHash,
286 event_index: u64,
288 },
289}
290
291impl Proof {
292 pub fn account_id(&self) -> AccountId {
294 match self {
295 Proof::Sr25519 { signer, .. } => *signer,
296 Proof::Ed25519 { signer, .. } => *signer,
297 Proof::Secp256k1Ecdsa { signer, .. } => {
298 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer).into()
299 },
300 Proof::OnChain { who, .. } => *who,
301 }
302 }
303}
304
305#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
308#[repr(u8)]
309pub enum Field {
310 AuthenticityProof(Proof) = 0,
312 DecryptionKey(DecryptionKey) = 1,
314 Expiry(u64) = 2,
316 Channel(Channel) = 3,
318 Topic1(Topic) = 4,
320 Topic2(Topic) = 5,
322 Topic3(Topic) = 6,
324 Topic4(Topic) = 7,
326 Data(Vec<u8>) = 8,
328}
329
330impl Field {
331 fn discriminant(&self) -> u8 {
332 unsafe { *(self as *const Self as *const u8) }
335 }
336}
337
338#[derive(DecodeWithMemTracking, Debug, Clone, PartialEq, Eq, Default)]
340pub struct Statement {
341 proof: Option<Proof>,
343 #[deprecated(note = "Experimental feature, may be removed/changed in future releases")]
345 decryption_key: Option<DecryptionKey>,
346 channel: Option<Channel>,
357 expiry: u64,
372 num_topics: u8,
374 topics: [Topic; MAX_TOPICS],
376 data: Option<Vec<u8>>,
378}
379
380impl TypeInfo for Statement {
383 type Identity = Self;
384
385 fn type_info() -> Type {
386 Type::builder()
388 .path(Path::new("Statement", module_path!()))
389 .docs(&["Statement structure"])
390 .composite(Fields::unnamed().field(|f| f.ty::<Vec<Field>>()))
391 }
392}
393
394impl Decode for Statement {
395 fn decode<I: codec::Input>(input: &mut I) -> core::result::Result<Self, codec::Error> {
396 let num_fields: codec::Compact<u32> = Decode::decode(input)?;
399 let mut tag = 0;
400 let mut statement = Statement::new();
401 for i in 0..num_fields.into() {
402 let field: Field = Decode::decode(input)?;
403 if i > 0 && field.discriminant() <= tag {
404 return Err("Invalid field order or duplicate fields".into());
405 }
406 tag = field.discriminant();
407 match field {
408 Field::AuthenticityProof(p) => statement.set_proof(p),
409 Field::DecryptionKey(key) => statement.set_decryption_key(key),
410 Field::Expiry(p) => statement.set_expiry(p),
411 Field::Channel(c) => statement.set_channel(c),
412 Field::Topic1(t) => statement.set_topic(0, t),
413 Field::Topic2(t) => statement.set_topic(1, t),
414 Field::Topic3(t) => statement.set_topic(2, t),
415 Field::Topic4(t) => statement.set_topic(3, t),
416 Field::Data(data) => statement.set_plain_data(data),
417 }
418 }
419 Ok(statement)
420 }
421}
422
423impl Encode for Statement {
424 fn encode(&self) -> Vec<u8> {
425 self.encoded(false)
426 }
427}
428
429#[derive(Clone, Copy, PartialEq, Eq, Debug)]
430pub enum SignatureVerificationResult {
432 Valid(AccountId),
434 Invalid,
436 NoSignature,
438}
439
440impl Statement {
441 pub fn new() -> Statement {
443 Default::default()
444 }
445
446 pub fn new_with_proof(proof: Proof) -> Statement {
448 let mut statement = Self::new();
449 statement.set_proof(proof);
450 statement
451 }
452
453 pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool {
459 let to_sign = self.signature_material();
460 if let Some(signature) = key.sign(&to_sign) {
461 let proof = Proof::Sr25519 {
462 signature: signature.into_inner().into(),
463 signer: key.clone().into_inner().into(),
464 };
465 self.set_proof(proof);
466 true
467 } else {
468 false
469 }
470 }
471
472 pub fn topics(&self) -> &[Topic] {
474 &self.topics[..self.num_topics as usize]
475 }
476
477 #[cfg(feature = "std")]
479 pub fn sign_sr25519_private(&mut self, key: &sp_core::sr25519::Pair) {
480 let to_sign = self.signature_material();
481 let proof =
482 Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
483 self.set_proof(proof);
484 }
485
486 pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool {
492 let to_sign = self.signature_material();
493 if let Some(signature) = key.sign(&to_sign) {
494 let proof = Proof::Ed25519 {
495 signature: signature.into_inner().into(),
496 signer: key.clone().into_inner().into(),
497 };
498 self.set_proof(proof);
499 true
500 } else {
501 false
502 }
503 }
504
505 #[cfg(feature = "std")]
507 pub fn sign_ed25519_private(&mut self, key: &sp_core::ed25519::Pair) {
508 let to_sign = self.signature_material();
509 let proof =
510 Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
511 self.set_proof(proof);
512 }
513
514 pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool {
524 let to_sign = self.signature_material();
525 if let Some(signature) = key.sign(&to_sign) {
526 let proof = Proof::Secp256k1Ecdsa {
527 signature: signature.into_inner().into(),
528 signer: key.clone().into_inner().0,
529 };
530 self.set_proof(proof);
531 true
532 } else {
533 false
534 }
535 }
536
537 #[cfg(feature = "std")]
539 pub fn sign_ecdsa_private(&mut self, key: &sp_core::ecdsa::Pair) {
540 let to_sign = self.signature_material();
541 let proof =
542 Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 };
543 self.set_proof(proof);
544 }
545
546 pub fn verify_signature(&self) -> SignatureVerificationResult {
548 use sp_runtime::traits::Verify;
549
550 match self.proof() {
551 Some(Proof::OnChain { .. }) | None => SignatureVerificationResult::NoSignature,
552 Some(Proof::Sr25519 { signature, signer }) => {
553 let to_sign = self.signature_material();
554 let signature = sp_core::sr25519::Signature::from(*signature);
555 let public = sp_core::sr25519::Public::from(*signer);
556 if signature.verify(to_sign.as_slice(), &public) {
557 SignatureVerificationResult::Valid(*signer)
558 } else {
559 SignatureVerificationResult::Invalid
560 }
561 },
562 Some(Proof::Ed25519 { signature, signer }) => {
563 let to_sign = self.signature_material();
564 let signature = sp_core::ed25519::Signature::from(*signature);
565 let public = sp_core::ed25519::Public::from(*signer);
566 if signature.verify(to_sign.as_slice(), &public) {
567 SignatureVerificationResult::Valid(*signer)
568 } else {
569 SignatureVerificationResult::Invalid
570 }
571 },
572 Some(Proof::Secp256k1Ecdsa { signature, signer }) => {
573 let to_sign = self.signature_material();
574 let signature = sp_core::ecdsa::Signature::from(*signature);
575 let public = sp_core::ecdsa::Public::from(*signer);
576 if signature.verify(to_sign.as_slice(), &public) {
577 let sender_hash =
578 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer);
579 SignatureVerificationResult::Valid(sender_hash.into())
580 } else {
581 SignatureVerificationResult::Invalid
582 }
583 },
584 }
585 }
586
587 #[cfg(feature = "std")]
589 pub fn hash(&self) -> [u8; 32] {
590 self.using_encoded(hash_encoded)
591 }
592
593 pub fn topic(&self, index: usize) -> Option<Topic> {
595 if index < self.num_topics as usize {
596 Some(self.topics[index])
597 } else {
598 None
599 }
600 }
601
602 #[allow(deprecated)]
604 pub fn decryption_key(&self) -> Option<DecryptionKey> {
605 self.decryption_key
606 }
607
608 pub fn into_data(self) -> Option<Vec<u8>> {
610 self.data
611 }
612
613 pub fn proof(&self) -> Option<&Proof> {
615 self.proof.as_ref()
616 }
617
618 pub fn account_id(&self) -> Option<AccountId> {
620 self.proof.as_ref().map(Proof::account_id)
621 }
622
623 pub fn data(&self) -> Option<&Vec<u8>> {
625 self.data.as_ref()
626 }
627
628 pub fn data_len(&self) -> usize {
630 self.data().map_or(0, Vec::len)
631 }
632
633 pub fn channel(&self) -> Option<Channel> {
635 self.channel
636 }
637
638 pub fn expiry(&self) -> u64 {
640 self.expiry
641 }
642
643 pub fn get_expiration_timestamp_secs(&self) -> u32 {
648 (self.expiry >> 32) as u32
649 }
650
651 fn signature_material(&self) -> Vec<u8> {
653 self.encoded(true)
654 }
655
656 pub fn remove_proof(&mut self) {
658 self.proof = None;
659 }
660
661 pub fn set_proof(&mut self, proof: Proof) {
663 self.proof = Some(proof)
664 }
665
666 pub fn set_expiry(&mut self, expiry: u64) {
668 self.expiry = expiry;
669 }
670
671 pub fn set_expiry_from_parts(&mut self, expiration_timestamp_secs: u32, sequence_number: u32) {
673 self.expiry = (expiration_timestamp_secs as u64) << 32 | sequence_number as u64;
674 }
675
676 pub fn set_channel(&mut self, channel: Channel) {
678 self.channel = Some(channel)
679 }
680
681 pub fn set_topic(&mut self, index: usize, topic: Topic) {
683 if index < MAX_TOPICS {
684 self.topics[index] = topic;
685 self.num_topics = self.num_topics.max(index as u8 + 1);
686 }
687 }
688
689 #[allow(deprecated)]
691 pub fn set_decryption_key(&mut self, key: DecryptionKey) {
692 self.decryption_key = Some(key);
693 }
694
695 pub fn set_plain_data(&mut self, data: Vec<u8>) {
697 self.data = Some(data)
698 }
699
700 #[allow(deprecated)]
712 fn estimated_encoded_size(&self, for_signing: bool) -> usize {
713 let proof_size =
714 if !for_signing && self.proof.is_some() { 1 + Proof::max_encoded_len() } else { 0 };
715 let decryption_key_size =
716 if self.decryption_key.is_some() { 1 + DecryptionKey::max_encoded_len() } else { 0 };
717 let expiry_size = 1 + u64::max_encoded_len();
718 let channel_size = if self.channel.is_some() { 1 + Channel::max_encoded_len() } else { 0 };
719 let topics_size = self.num_topics as usize * (1 + Topic::max_encoded_len());
720 let data_size = self
721 .data
722 .as_ref()
723 .map_or(0, |d| 1 + Compact::<u32>::max_encoded_len() + d.len());
724 let compact_prefix_size = if !for_signing { Compact::<u32>::max_encoded_len() } else { 0 };
725
726 compact_prefix_size +
727 proof_size +
728 decryption_key_size +
729 expiry_size +
730 channel_size +
731 topics_size +
732 data_size
733 }
734
735 #[allow(deprecated)]
736 fn encoded(&self, for_signing: bool) -> Vec<u8> {
737 let num_fields = if !for_signing && self.proof.is_some() { 2 } else { 1 } +
741 if self.decryption_key.is_some() { 1 } else { 0 } +
742 if self.channel.is_some() { 1 } else { 0 } +
743 if self.data.is_some() { 1 } else { 0 } +
744 self.num_topics as u32;
745
746 let mut output = Vec::with_capacity(self.estimated_encoded_size(for_signing));
747 if !for_signing {
751 let compact_len = codec::Compact::<u32>(num_fields);
752 compact_len.encode_to(&mut output);
753
754 if let Some(proof) = &self.proof {
755 0u8.encode_to(&mut output);
756 proof.encode_to(&mut output);
757 }
758 }
759 if let Some(decryption_key) = &self.decryption_key {
760 1u8.encode_to(&mut output);
761 decryption_key.encode_to(&mut output);
762 }
763
764 2u8.encode_to(&mut output);
765 self.expiry().encode_to(&mut output);
766
767 if let Some(channel) = &self.channel {
768 3u8.encode_to(&mut output);
769 channel.encode_to(&mut output);
770 }
771 for t in 0..self.num_topics {
772 (4u8 + t).encode_to(&mut output);
773 self.topics[t as usize].encode_to(&mut output);
774 }
775 if let Some(data) = &self.data {
776 8u8.encode_to(&mut output);
777 data.encode_to(&mut output);
778 }
779 output
780 }
781
782 #[allow(deprecated)]
784 #[cfg(feature = "std")]
785 pub fn encrypt(
786 &mut self,
787 data: &[u8],
788 key: &sp_core::ed25519::Public,
789 ) -> core::result::Result<(), ecies::Error> {
790 let encrypted = ecies::encrypt_ed25519(key, data)?;
791 self.data = Some(encrypted);
792 self.decryption_key = Some((*key).into());
793 Ok(())
794 }
795
796 #[cfg(feature = "std")]
798 pub fn decrypt_private(
799 &self,
800 key: &sp_core::ed25519::Pair,
801 ) -> core::result::Result<Option<Vec<u8>>, ecies::Error> {
802 self.data.as_ref().map(|d| ecies::decrypt_ed25519(key, d)).transpose()
803 }
804}
805
806#[cfg(test)]
807mod test {
808 use crate::{
809 hash_encoded, Field, Proof, SignatureVerificationResult, Statement, Topic, MAX_TOPICS,
810 };
811 use codec::{Decode, Encode};
812 use scale_info::{MetaType, TypeInfo};
813 use sp_application_crypto::Pair;
814 use sp_core::sr25519;
815
816 #[test]
817 fn statement_encoding_matches_vec() {
818 let mut statement = Statement::new();
819 assert!(statement.proof().is_none());
820 let proof = Proof::OnChain { who: [42u8; 32], block_hash: [24u8; 32], event_index: 66 };
821
822 let decryption_key = [0xde; 32];
823 let topic1: Topic = [0x01; 32].into();
824 let topic2: Topic = [0x02; 32].into();
825 let data = vec![55, 99];
826 let expiry = 999;
827 let channel = [0xcc; 32];
828
829 statement.set_proof(proof.clone());
830 statement.set_decryption_key(decryption_key);
831 statement.set_expiry(expiry);
832 statement.set_channel(channel);
833 statement.set_topic(0, topic1);
834 statement.set_topic(1, topic2);
835 statement.set_plain_data(data.clone());
836
837 statement.set_topic(5, [0x55; 32].into());
838 assert_eq!(statement.topic(5), None);
839
840 let fields = vec![
841 Field::AuthenticityProof(proof.clone()),
842 Field::DecryptionKey(decryption_key),
843 Field::Expiry(expiry),
844 Field::Channel(channel),
845 Field::Topic1(topic1),
846 Field::Topic2(topic2),
847 Field::Data(data.clone()),
848 ];
849
850 let encoded = statement.encode();
851 assert_eq!(statement.hash(), hash_encoded(&encoded));
852 assert_eq!(encoded, fields.encode());
853
854 let decoded = Statement::decode(&mut encoded.as_slice()).unwrap();
855 assert_eq!(decoded, statement);
856 }
857
858 #[test]
859 fn decode_checks_fields() {
860 let topic1: Topic = [0x01; 32].into();
861 let topic2: Topic = [0x02; 32].into();
862 let priority = 999;
863
864 let fields = vec![
865 Field::Expiry(priority),
866 Field::Topic1(topic1),
867 Field::Topic1(topic1),
868 Field::Topic2(topic2),
869 ]
870 .encode();
871
872 assert!(Statement::decode(&mut fields.as_slice()).is_err());
873
874 let fields =
875 vec![Field::Topic1(topic1), Field::Expiry(priority), Field::Topic2(topic2)].encode();
876
877 assert!(Statement::decode(&mut fields.as_slice()).is_err());
878 }
879
880 #[test]
881 fn sign_and_verify() {
882 let mut statement = Statement::new();
883 statement.set_plain_data(vec![42]);
884
885 let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
886 let ed25519_kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap();
887 let secp256k1_kp = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
888
889 statement.sign_sr25519_private(&sr25519_kp);
890 assert_eq!(
891 statement.verify_signature(),
892 SignatureVerificationResult::Valid(sr25519_kp.public().0)
893 );
894
895 statement.sign_ed25519_private(&ed25519_kp);
896 assert_eq!(
897 statement.verify_signature(),
898 SignatureVerificationResult::Valid(ed25519_kp.public().0)
899 );
900
901 statement.sign_ecdsa_private(&secp256k1_kp);
902 assert_eq!(
903 statement.verify_signature(),
904 SignatureVerificationResult::Valid(sp_crypto_hashing::blake2_256(
905 &secp256k1_kp.public().0
906 ))
907 );
908
909 statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
911 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
912
913 statement.remove_proof();
914 assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature);
915 }
916
917 #[test]
918 fn encrypt_decrypt() {
919 let mut statement = Statement::new();
920 let (pair, _) = sp_core::ed25519::Pair::generate();
921 let plain = b"test data".to_vec();
922
923 statement.encrypt(&plain, &pair.public()).unwrap();
925 assert_ne!(plain.as_slice(), statement.data().unwrap().as_slice());
926
927 let decrypted = statement.decrypt_private(&pair).unwrap();
928 assert_eq!(decrypted, Some(plain));
929 }
930
931 #[test]
932 fn check_matches() {
933 let mut statement = Statement::new();
934 let topic1: Topic = [0x01; 32].into();
935 let topic2: Topic = [0x02; 32].into();
936 let topic3: Topic = [0x03; 32].into();
937
938 statement.set_topic(0, topic1);
939 statement.set_topic(1, topic2);
940
941 let filter_any = crate::OptimizedTopicFilter::Any;
942 assert!(filter_any.matches(&statement));
943
944 let filter_all =
945 crate::OptimizedTopicFilter::MatchAll([topic1, topic2].iter().cloned().collect());
946 assert!(filter_all.matches(&statement));
947
948 let filter_all_fail =
949 crate::OptimizedTopicFilter::MatchAll([topic1, topic3].iter().cloned().collect());
950 assert!(!filter_all_fail.matches(&statement));
951
952 let filter_any_match =
953 crate::OptimizedTopicFilter::MatchAny([topic2, topic3].iter().cloned().collect());
954 assert!(filter_any_match.matches(&statement));
955
956 let filter_any_fail =
957 crate::OptimizedTopicFilter::MatchAny([topic3].iter().cloned().collect());
958 assert!(!filter_any_fail.matches(&statement));
959 }
960
961 #[test]
962 fn statement_type_info_matches_encoding() {
963 let statement_type = Statement::type_info();
966 let vec_field_meta = MetaType::new::<Vec<Field>>();
967
968 match statement_type.type_def {
970 scale_info::TypeDef::Composite(composite) => {
971 assert_eq!(composite.fields.len(), 1, "Statement should have exactly one field");
972 let field = &composite.fields[0];
973 assert!(field.name.is_none(), "Field should be unnamed (newtype pattern)");
974 assert_eq!(field.ty, vec_field_meta, "Statement's inner type should be Vec<Field>");
975 },
976 _ => panic!("Statement TypeInfo should be a Composite"),
977 }
978 }
979
980 #[test]
981 fn measure_hash_30_000_statements() {
982 use std::time::Instant;
983 const NUM_STATEMENTS: usize = 30_000;
984 let (keyring, _) = sr25519::Pair::generate();
985
986 let statements: Vec<Statement> = (0..NUM_STATEMENTS)
988 .map(|i| {
989 let mut statement = Statement::new();
990
991 statement.set_expiry(i as u64);
992 statement.set_topic(0, [(i % 256) as u8; 32].into());
993 statement.set_plain_data(vec![i as u8; 512]);
994 statement.sign_sr25519_private(&keyring);
995
996 statement.sign_sr25519_private(&keyring);
997 statement
998 })
999 .collect();
1000 let start = Instant::now();
1002 let hashes: Vec<[u8; 32]> = statements.iter().map(|s| s.hash()).collect();
1003 let elapsed = start.elapsed();
1004 println!("Time to hash {} statements: {:?}", NUM_STATEMENTS, elapsed);
1005 println!("Average time per statement: {:?}", elapsed / NUM_STATEMENTS as u32);
1006 let unique_hashes: std::collections::HashSet<_> = hashes.iter().collect();
1008 assert_eq!(unique_hashes.len(), NUM_STATEMENTS);
1009 }
1010
1011 #[test]
1012 fn estimated_encoded_size_is_sufficient() {
1013 const MAX_ACCEPTED_OVERHEAD: usize = 33;
1015
1016 let proof = Proof::OnChain { who: [42u8; 32], block_hash: [24u8; 32], event_index: 66 };
1017 let decryption_key = [0xde; 32];
1018 let data = vec![55; 1000];
1019 let expiry = 999;
1020 let channel = [0xcc; 32];
1021
1022 let mut statement = Statement::new();
1024 statement.set_proof(proof);
1025 statement.set_decryption_key(decryption_key);
1026 statement.set_expiry(expiry);
1027 statement.set_channel(channel);
1028 for i in 0..MAX_TOPICS {
1029 statement.set_topic(i, [i as u8; 32].into());
1030 }
1031 statement.set_plain_data(data);
1032
1033 let encoded = statement.encode();
1034 let estimated = statement.estimated_encoded_size(false);
1035 assert!(
1036 estimated >= encoded.len(),
1037 "estimated_encoded_size ({}) should be >= actual encoded length ({})",
1038 estimated,
1039 encoded.len()
1040 );
1041 let overhead = estimated - encoded.len();
1042 assert!(
1043 overhead <= MAX_ACCEPTED_OVERHEAD,
1044 "estimated overhead ({}) should be small, estimated: {}, actual: {}",
1045 overhead,
1046 estimated,
1047 encoded.len()
1048 );
1049
1050 let signing_payload = statement.encoded(true);
1052 let signing_estimated = statement.estimated_encoded_size(true);
1053 assert!(
1054 signing_estimated >= signing_payload.len(),
1055 "estimated_encoded_size for signing ({}) should be >= actual signing payload length ({})",
1056 signing_estimated,
1057 signing_payload.len()
1058 );
1059 let signing_overhead = signing_estimated - signing_payload.len();
1060 assert!(
1061 signing_overhead <= MAX_ACCEPTED_OVERHEAD,
1062 "signing overhead ({}) should be small, estimated: {}, actual: {}",
1063 signing_overhead,
1064 signing_estimated,
1065 signing_payload.len()
1066 );
1067
1068 let empty_statement = Statement::new();
1070 let empty_encoded = empty_statement.encode();
1071 let empty_estimated = empty_statement.estimated_encoded_size(false);
1072 assert!(
1073 empty_estimated >= empty_encoded.len(),
1074 "estimated_encoded_size for empty ({}) should be >= actual encoded length ({})",
1075 empty_estimated,
1076 empty_encoded.len()
1077 );
1078 let empty_overhead = empty_estimated - empty_encoded.len();
1079 assert!(
1080 empty_overhead <= MAX_ACCEPTED_OVERHEAD,
1081 "empty overhead ({}) should be minimal, estimated: {}, actual: {}",
1082 empty_overhead,
1083 empty_estimated,
1084 empty_encoded.len()
1085 );
1086 }
1087}