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];
126
127pub const MAX_TOPICS: usize = 4;
129pub const MAX_ANY_TOPICS: usize = 128;
132
133#[derive(Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
148pub struct StatementAllowance {
149 pub max_count: u32,
151 pub max_size: u32,
153}
154
155impl StatementAllowance {
156 pub fn new(max_count: u32, max_size: u32) -> Self {
158 Self { max_count, max_size }
159 }
160
161 pub const fn saturating_add(self, rhs: StatementAllowance) -> StatementAllowance {
163 StatementAllowance {
164 max_count: self.max_count.saturating_add(rhs.max_count),
165 max_size: self.max_size.saturating_add(rhs.max_size),
166 }
167 }
168
169 pub const fn saturating_sub(self, rhs: StatementAllowance) -> StatementAllowance {
171 StatementAllowance {
172 max_count: self.max_count.saturating_sub(rhs.max_count),
173 max_size: self.max_size.saturating_sub(rhs.max_size),
174 }
175 }
176
177 pub fn is_depleted(&self) -> bool {
180 self.max_count == 0 || self.max_size == 0
181 }
182}
183
184pub const STATEMENT_ALLOWANCE_PREFIX: &[u8] = b":statement_allowance:";
186
187pub fn statement_allowance_key(account_id: impl AsRef<[u8]>) -> Vec<u8> {
195 let mut key = STATEMENT_ALLOWANCE_PREFIX.to_vec();
196 key.extend_from_slice(account_id.as_ref());
197 key
198}
199
200pub fn increase_allowance_by(account_id: impl AsRef<[u8]>, by: StatementAllowance) {
202 let key = statement_allowance_key(account_id);
203 let mut allowance: StatementAllowance = frame_support::storage::unhashed::get_or_default(&key);
204 allowance = allowance.saturating_add(by);
205 frame_support::storage::unhashed::put(&key, &allowance);
206}
207
208pub fn decrease_allowance_by(account_id: impl AsRef<[u8]>, by: StatementAllowance) {
210 let key = statement_allowance_key(account_id);
211 let mut allowance: StatementAllowance = frame_support::storage::unhashed::get_or_default(&key);
212 allowance = allowance.saturating_sub(by);
213 if allowance.is_depleted() {
214 frame_support::storage::unhashed::kill(&key);
215 } else {
216 frame_support::storage::unhashed::put(&key, &allowance);
217 }
218}
219
220pub fn get_allowance(account_id: impl AsRef<[u8]>) -> StatementAllowance {
222 let key = statement_allowance_key(account_id);
223 frame_support::storage::unhashed::get_or_default(&key)
224}
225
226pub use event::{
227 AddFilterResponse, LimitReachedResult, LimitReachedTag, NewStatementEntry, SubscribeEvent,
228};
229#[cfg(feature = "std")]
230pub use store_api::{
231 Error, FilterDecision, FilterId, InvalidReason, LiveStatementEvent, OptimizedTopicFilter,
232 RejectionReason, Result, StatementEvent, StatementSource, StatementStore, SubmitInvalidReason,
233 SubmitOutcome, SubmitRejectionReason, SubmitResult, TopicFilter,
234};
235
236#[cfg(feature = "std")]
237mod ecies;
238mod event;
239pub mod runtime_api;
240#[cfg(feature = "std")]
241mod store_api;
242
243mod sr25519 {
244 mod app_sr25519 {
245 use sp_application_crypto::{app_crypto, key_types::STATEMENT, sr25519};
246 app_crypto!(sr25519, STATEMENT);
247 }
248 pub type Public = app_sr25519::Public;
249}
250
251pub mod ed25519 {
253 mod app_ed25519 {
254 use sp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT};
255 app_crypto!(ed25519, STATEMENT);
256 }
257 pub type Public = app_ed25519::Public;
259 #[cfg(feature = "std")]
261 pub type Pair = app_ed25519::Pair;
262}
263
264mod ecdsa {
265 mod app_ecdsa {
266 use sp_application_crypto::{app_crypto, ecdsa, key_types::STATEMENT};
267 app_crypto!(ecdsa, STATEMENT);
268 }
269 pub type Public = app_ecdsa::Public;
270}
271
272#[cfg(feature = "std")]
274pub fn hash_encoded(data: &[u8]) -> [u8; 32] {
275 sp_crypto_hashing::blake2_256(data)
276}
277
278#[derive(
280 Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, Debug, Clone, PartialEq, Eq,
281)]
282pub enum Proof {
283 Sr25519 {
285 signature: [u8; 64],
287 signer: [u8; 32],
289 },
290 Ed25519 {
292 signature: [u8; 64],
294 signer: [u8; 32],
296 },
297 Secp256k1Ecdsa {
299 signature: [u8; 65],
301 signer: [u8; 33],
303 },
304}
305
306impl Proof {
307 pub fn account_id(&self) -> AccountId {
309 match self {
310 Proof::Sr25519 { signer, .. } => *signer,
311 Proof::Ed25519 { signer, .. } => *signer,
312 Proof::Secp256k1Ecdsa { signer, .. } => {
313 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer).into()
314 },
315 }
316 }
317}
318
319#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
322#[repr(u8)]
323pub enum Field {
324 AuthenticityProof(Proof) = 0,
326 DecryptionKey(DecryptionKey) = 1,
328 Expiry(u64) = 2,
330 Channel(Channel) = 3,
332 Topic1(Topic) = 4,
334 Topic2(Topic) = 5,
336 Topic3(Topic) = 6,
338 Topic4(Topic) = 7,
340 Data(Vec<u8>) = 8,
342}
343
344impl Field {
345 fn discriminant(&self) -> u8 {
346 unsafe { *(self as *const Self as *const u8) }
349 }
350}
351
352#[derive(DecodeWithMemTracking, Debug, Clone, PartialEq, Eq, Default)]
354pub struct Statement {
355 proof: Option<Proof>,
357 #[deprecated(note = "Experimental feature, may be removed/changed in future releases")]
359 decryption_key: Option<DecryptionKey>,
360 channel: Option<Channel>,
371 expiry: u64,
386 num_topics: u8,
388 topics: [Topic; MAX_TOPICS],
390 data: Option<Vec<u8>>,
392}
393
394impl TypeInfo for Statement {
397 type Identity = Self;
398
399 fn type_info() -> Type {
400 Type::builder()
402 .path(Path::new("Statement", module_path!()))
403 .docs(&["Statement structure"])
404 .composite(Fields::unnamed().field(|f| f.ty::<Vec<Field>>()))
405 }
406}
407
408impl Decode for Statement {
409 fn decode<I: codec::Input>(input: &mut I) -> core::result::Result<Self, codec::Error> {
410 let num_fields: codec::Compact<u32> = Decode::decode(input)?;
413 let mut tag = 0;
414 let mut statement = Statement::new();
415 for i in 0..num_fields.into() {
416 let field: Field = Decode::decode(input)?;
417 if i > 0 && field.discriminant() <= tag {
418 return Err("Invalid field order or duplicate fields".into());
419 }
420 tag = field.discriminant();
421 match field {
422 Field::AuthenticityProof(p) => statement.set_proof(p),
423 Field::DecryptionKey(key) => statement.set_decryption_key(key),
424 Field::Expiry(p) => statement.set_expiry(p),
425 Field::Channel(c) => statement.set_channel(c),
426 Field::Topic1(t) => statement.set_topic(0, t),
427 Field::Topic2(t) => statement.set_topic(1, t),
428 Field::Topic3(t) => statement.set_topic(2, t),
429 Field::Topic4(t) => statement.set_topic(3, t),
430 Field::Data(data) => statement.set_plain_data(data),
431 }
432 }
433 Ok(statement)
434 }
435}
436
437impl Encode for Statement {
438 fn encode(&self) -> Vec<u8> {
439 self.encoded(false)
440 }
441}
442
443#[derive(Clone, Copy, PartialEq, Eq, Debug)]
444pub enum SignatureVerificationResult {
446 Valid(AccountId),
448 Invalid,
450 NoSignature,
452}
453
454impl Statement {
455 pub fn new() -> Statement {
457 Default::default()
458 }
459
460 pub fn new_with_proof(proof: Proof) -> Statement {
462 let mut statement = Self::new();
463 statement.set_proof(proof);
464 statement
465 }
466
467 pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool {
473 let to_sign = self.signature_material();
474 if let Some(signature) = key.sign(&to_sign) {
475 let proof = Proof::Sr25519 {
476 signature: signature.into_inner().into(),
477 signer: key.clone().into_inner().into(),
478 };
479 self.set_proof(proof);
480 true
481 } else {
482 false
483 }
484 }
485
486 pub fn topics(&self) -> &[Topic] {
488 &self.topics[..self.num_topics as usize]
489 }
490
491 #[cfg(feature = "std")]
493 pub fn sign_sr25519_private(&mut self, key: &sp_core::sr25519::Pair) {
494 let to_sign = self.signature_material();
495 let proof =
496 Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
497 self.set_proof(proof);
498 }
499
500 pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool {
506 let to_sign = self.signature_material();
507 if let Some(signature) = key.sign(&to_sign) {
508 let proof = Proof::Ed25519 {
509 signature: signature.into_inner().into(),
510 signer: key.clone().into_inner().into(),
511 };
512 self.set_proof(proof);
513 true
514 } else {
515 false
516 }
517 }
518
519 #[cfg(feature = "std")]
521 pub fn sign_ed25519_private(&mut self, key: &sp_core::ed25519::Pair) {
522 let to_sign = self.signature_material();
523 let proof =
524 Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
525 self.set_proof(proof);
526 }
527
528 pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool {
534 let to_sign = self.signature_material();
535 if let Some(signature) = key.sign(&to_sign) {
536 let proof = Proof::Secp256k1Ecdsa {
537 signature: signature.into_inner().into(),
538 signer: key.clone().into_inner().0,
539 };
540 self.set_proof(proof);
541 true
542 } else {
543 false
544 }
545 }
546
547 #[cfg(feature = "std")]
549 pub fn sign_ecdsa_private(&mut self, key: &sp_core::ecdsa::Pair) {
550 let to_sign = self.signature_material();
551 let proof =
552 Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 };
553 self.set_proof(proof);
554 }
555
556 pub fn verify_signature(&self) -> SignatureVerificationResult {
563 use sp_runtime::traits::Verify;
564
565 match self.proof() {
566 None => SignatureVerificationResult::NoSignature,
567 Some(Proof::Sr25519 { signature, signer }) => {
568 let to_sign = self.signature_material();
569 let signature = sp_core::sr25519::Signature::from(*signature);
570 let public = sp_core::sr25519::Public::from(*signer);
571 if signature.verify(to_sign.as_slice(), &public) {
572 SignatureVerificationResult::Valid(*signer)
573 } else {
574 SignatureVerificationResult::Invalid
575 }
576 },
577 Some(Proof::Ed25519 { signature, signer }) => {
578 let to_sign = self.signature_material();
579 let signature = sp_core::ed25519::Signature::from(*signature);
580 let public = sp_core::ed25519::Public::from(*signer);
581 if signature.verify(to_sign.as_slice(), &public) {
582 SignatureVerificationResult::Valid(*signer)
583 } else {
584 SignatureVerificationResult::Invalid
585 }
586 },
587 Some(Proof::Secp256k1Ecdsa { signature, signer }) => {
588 let to_sign = self.signature_material();
589 let signature = sp_core::ecdsa::Signature::from(*signature);
590 let public = sp_core::ecdsa::Public::from(*signer);
591 if signature.verify(to_sign.as_slice(), &public) {
592 let sender_hash =
593 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer);
594 SignatureVerificationResult::Valid(sender_hash.into())
595 } else {
596 SignatureVerificationResult::Invalid
597 }
598 },
599 }
600 }
601
602 #[cfg(feature = "std")]
608 pub fn hash(&self) -> [u8; 32] {
609 self.using_encoded(hash_encoded)
610 }
611
612 pub fn topic(&self, index: usize) -> Option<Topic> {
614 if index < self.num_topics as usize {
615 Some(self.topics[index])
616 } else {
617 None
618 }
619 }
620
621 #[allow(deprecated)]
623 pub fn decryption_key(&self) -> Option<DecryptionKey> {
624 self.decryption_key
625 }
626
627 pub fn into_data(self) -> Option<Vec<u8>> {
629 self.data
630 }
631
632 pub fn proof(&self) -> Option<&Proof> {
634 self.proof.as_ref()
635 }
636
637 pub fn account_id(&self) -> Option<AccountId> {
639 self.proof.as_ref().map(Proof::account_id)
640 }
641
642 pub fn data(&self) -> Option<&Vec<u8>> {
646 self.data.as_ref()
647 }
648
649 pub fn data_len(&self) -> usize {
654 self.data().map_or(0, Vec::len)
655 }
656
657 pub fn channel(&self) -> Option<Channel> {
659 self.channel
660 }
661
662 pub fn expiry(&self) -> u64 {
664 self.expiry
665 }
666
667 pub fn get_expiration_timestamp_secs(&self) -> u32 {
672 (self.expiry >> 32) as u32
673 }
674
675 fn signature_material(&self) -> Vec<u8> {
677 self.encoded(true)
678 }
679
680 pub fn remove_proof(&mut self) {
682 self.proof = None;
683 }
684
685 pub fn set_proof(&mut self, proof: Proof) {
687 self.proof = Some(proof)
688 }
689
690 pub fn set_expiry(&mut self, expiry: u64) {
692 self.expiry = expiry;
693 }
694
695 pub fn set_expiry_from_parts(&mut self, expiration_timestamp_secs: u32, sequence_number: u32) {
697 self.expiry = (expiration_timestamp_secs as u64) << 32 | sequence_number as u64;
698 }
699
700 pub fn set_channel(&mut self, channel: Channel) {
702 self.channel = Some(channel)
703 }
704
705 pub fn set_topic(&mut self, index: usize, topic: Topic) {
709 if index < MAX_TOPICS {
710 self.topics[index] = topic;
711 self.num_topics = self.num_topics.max(index as u8 + 1);
712 }
713 }
714
715 #[allow(deprecated)]
717 pub fn set_decryption_key(&mut self, key: DecryptionKey) {
718 self.decryption_key = Some(key);
719 }
720
721 pub fn set_plain_data(&mut self, data: Vec<u8>) {
723 self.data = Some(data)
724 }
725
726 #[allow(deprecated)]
738 fn estimated_encoded_size(&self, for_signing: bool) -> usize {
739 let proof_size =
740 if !for_signing && self.proof.is_some() { 1 + Proof::max_encoded_len() } else { 0 };
741 let decryption_key_size =
742 if self.decryption_key.is_some() { 1 + DecryptionKey::max_encoded_len() } else { 0 };
743 let expiry_size = 1 + u64::max_encoded_len();
744 let channel_size = if self.channel.is_some() { 1 + Channel::max_encoded_len() } else { 0 };
745 let topics_size = self.num_topics as usize * (1 + Topic::max_encoded_len());
746 let data_size = self
747 .data
748 .as_ref()
749 .map_or(0, |d| 1 + Compact::<u32>::max_encoded_len() + d.len());
750 let compact_prefix_size = if !for_signing { Compact::<u32>::max_encoded_len() } else { 0 };
751
752 compact_prefix_size +
753 proof_size +
754 decryption_key_size +
755 expiry_size +
756 channel_size +
757 topics_size +
758 data_size
759 }
760
761 #[allow(deprecated)]
762 fn encoded(&self, for_signing: bool) -> Vec<u8> {
763 let num_fields = if !for_signing && self.proof.is_some() { 2 } else { 1 } +
767 if self.decryption_key.is_some() { 1 } else { 0 } +
768 if self.channel.is_some() { 1 } else { 0 } +
769 if self.data.is_some() { 1 } else { 0 } +
770 self.num_topics as u32;
771
772 let mut output = Vec::with_capacity(self.estimated_encoded_size(for_signing));
773 if !for_signing {
777 let compact_len = codec::Compact::<u32>(num_fields);
778 compact_len.encode_to(&mut output);
779
780 if let Some(proof) = &self.proof {
781 0u8.encode_to(&mut output);
782 proof.encode_to(&mut output);
783 }
784 }
785 if let Some(decryption_key) = &self.decryption_key {
786 1u8.encode_to(&mut output);
787 decryption_key.encode_to(&mut output);
788 }
789
790 2u8.encode_to(&mut output);
791 self.expiry().encode_to(&mut output);
792
793 if let Some(channel) = &self.channel {
794 3u8.encode_to(&mut output);
795 channel.encode_to(&mut output);
796 }
797 for t in 0..self.num_topics {
798 (4u8 + t).encode_to(&mut output);
799 self.topics[t as usize].encode_to(&mut output);
800 }
801 if let Some(data) = &self.data {
802 8u8.encode_to(&mut output);
803 data.encode_to(&mut output);
804 }
805 output
806 }
807
808 #[allow(deprecated)]
813 #[cfg(feature = "std")]
814 pub fn encrypt(
815 &mut self,
816 data: &[u8],
817 key: &sp_core::ed25519::Public,
818 ) -> core::result::Result<(), ecies::Error> {
819 let encrypted = ecies::encrypt_ed25519(key, data)?;
820 self.data = Some(encrypted);
821 self.decryption_key = Some((*key).into());
822 Ok(())
823 }
824
825 #[cfg(feature = "std")]
830 pub fn decrypt_private(
831 &self,
832 key: &sp_core::ed25519::Pair,
833 ) -> core::result::Result<Option<Vec<u8>>, ecies::Error> {
834 self.data.as_ref().map(|d| ecies::decrypt_ed25519(key, d)).transpose()
835 }
836}
837
838#[cfg(test)]
839mod test {
840 use crate::{
841 hash_encoded, Field, Proof, SignatureVerificationResult, Statement, Topic, MAX_TOPICS,
842 };
843 use codec::{Decode, Encode, MaxEncodedLen};
844 use scale_info::{MetaType, TypeInfo};
845 use sp_application_crypto::Pair;
846 use sp_core::sr25519;
847
848 #[test]
849 fn statement_encoding_matches_vec() {
850 let mut statement = Statement::new();
851 assert!(statement.proof().is_none());
852 let proof = Proof::Sr25519 { signature: [42u8; 64], signer: [24u8; 32] };
853
854 let decryption_key = [0xde; 32];
855 let topic1: Topic = [0x01; 32].into();
856 let topic2: Topic = [0x02; 32].into();
857 let data = vec![55, 99];
858 let expiry = 999;
859 let channel = [0xcc; 32];
860
861 statement.set_proof(proof.clone());
862 statement.set_decryption_key(decryption_key);
863 statement.set_expiry(expiry);
864 statement.set_channel(channel);
865 statement.set_topic(0, topic1);
866 statement.set_topic(1, topic2);
867 statement.set_plain_data(data.clone());
868
869 statement.set_topic(5, [0x55; 32].into());
870 assert_eq!(statement.topic(5), None);
871
872 let fields = vec![
873 Field::AuthenticityProof(proof.clone()),
874 Field::DecryptionKey(decryption_key),
875 Field::Expiry(expiry),
876 Field::Channel(channel),
877 Field::Topic1(topic1),
878 Field::Topic2(topic2),
879 Field::Data(data.clone()),
880 ];
881
882 let encoded = statement.encode();
883 assert_eq!(statement.hash(), hash_encoded(&encoded));
884 assert_eq!(encoded, fields.encode());
885
886 let decoded = Statement::decode(&mut encoded.as_slice()).unwrap();
887 assert_eq!(decoded, statement);
888 }
889
890 #[test]
891 fn decode_checks_fields() {
892 let topic1: Topic = [0x01; 32].into();
893 let topic2: Topic = [0x02; 32].into();
894 let priority = 999;
895
896 let dup_topic1 = vec![
897 Field::Expiry(priority),
898 Field::Topic1(topic1),
899 Field::Topic1(topic1),
900 Field::Topic2(topic2),
901 ]
902 .encode();
903 assert!(Statement::decode(&mut dup_topic1.as_slice()).is_err());
904
905 let topic1_before_expiry =
906 vec![Field::Topic1(topic1), Field::Expiry(priority), Field::Topic2(topic2)].encode();
907 assert!(Statement::decode(&mut topic1_before_expiry.as_slice()).is_err());
908
909 let dup_expiry = vec![Field::Expiry(1), Field::Expiry(2)].encode();
910 assert!(Statement::decode(&mut dup_expiry.as_slice()).is_err());
911
912 let dup_data = vec![Field::Data(vec![1]), Field::Data(vec![2])].encode();
913 assert!(Statement::decode(&mut dup_data.as_slice()).is_err());
914
915 let data_before_expiry = vec![Field::Data(vec![1]), Field::Expiry(42)].encode();
916 assert!(Statement::decode(&mut data_before_expiry.as_slice()).is_err());
917
918 let channel_before_expiry = vec![Field::Channel([0; 32]), Field::Expiry(1)].encode();
919 assert!(Statement::decode(&mut channel_before_expiry.as_slice()).is_err());
920
921 let topic2_before_topic1 =
922 vec![Field::Expiry(1), Field::Topic2(topic1), Field::Topic1(topic2)].encode();
923 assert!(Statement::decode(&mut topic2_before_topic1.as_slice()).is_err());
924 }
925
926 #[test]
927 fn decode_rejects_malformed_bytes() {
928 assert!(Statement::decode(&mut &[][..]).is_err());
929
930 let valid = vec![Field::Expiry(42)].encode();
932 let decoded = Statement::decode(&mut valid.as_slice()).unwrap();
933 assert_eq!(decoded.expiry(), 42);
934
935 assert!(Statement::decode(&mut &valid[..1][..]).is_err());
937
938 let mut invalid_discriminant = valid.clone();
940 invalid_discriminant[1] = 9;
941 assert!(Statement::decode(&mut invalid_discriminant.as_slice()).is_err());
942
943 invalid_discriminant[1] = 255;
944 assert!(Statement::decode(&mut invalid_discriminant.as_slice()).is_err());
945
946 assert!(Statement::decode(&mut &valid[..5][..]).is_err());
948
949 let with_proof = vec![
951 Field::AuthenticityProof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] }),
952 Field::Expiry(42),
953 ]
954 .encode();
955 assert!(Statement::decode(&mut with_proof.as_slice()).is_ok());
956
957 let mut invalid_proof_variant = with_proof.clone();
958 invalid_proof_variant[2] = 99;
959 assert!(Statement::decode(&mut invalid_proof_variant.as_slice()).is_err());
960
961 assert!(Statement::decode(&mut &with_proof[..6][..]).is_err());
963
964 let mut inflated_count = valid.clone();
966 inflated_count[0] = 5 << 2; assert!(Statement::decode(&mut inflated_count.as_slice()).is_err());
968 }
969
970 #[test]
971 fn sign_and_verify() {
972 let mut statement = Statement::new();
973 statement.set_plain_data(vec![42]);
974
975 let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
976 let ed25519_kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap();
977 let secp256k1_kp = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
978
979 statement.sign_sr25519_private(&sr25519_kp);
980 assert_eq!(
981 statement.verify_signature(),
982 SignatureVerificationResult::Valid(sr25519_kp.public().0)
983 );
984
985 statement.sign_ed25519_private(&ed25519_kp);
986 assert_eq!(
987 statement.verify_signature(),
988 SignatureVerificationResult::Valid(ed25519_kp.public().0)
989 );
990
991 statement.sign_ecdsa_private(&secp256k1_kp);
992 assert_eq!(
993 statement.verify_signature(),
994 SignatureVerificationResult::Valid(sp_crypto_hashing::blake2_256(
995 &secp256k1_kp.public().0
996 ))
997 );
998
999 statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
1001 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
1002
1003 statement.set_proof(Proof::Ed25519 { signature: [0xAB; 64], signer: [0xCD; 32] });
1005 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
1006
1007 statement.set_proof(Proof::Secp256k1Ecdsa { signature: [0u8; 65], signer: [0u8; 33] });
1009 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
1010
1011 statement.remove_proof();
1012 assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature);
1013 }
1014
1015 #[test]
1016 fn encrypt_decrypt() {
1017 let mut statement = Statement::new();
1018 let (pair, _) = sp_core::ed25519::Pair::generate();
1019 let plain = b"test data".to_vec();
1020
1021 statement.encrypt(&plain, &pair.public()).unwrap();
1023 assert_ne!(plain.as_slice(), statement.data().unwrap().as_slice());
1024
1025 let decrypted = statement.decrypt_private(&pair).unwrap();
1026 assert_eq!(decrypted, Some(plain));
1027 }
1028
1029 #[test]
1030 fn check_matches() {
1031 let mut statement = Statement::new();
1032 let topic1: Topic = [0x01; 32].into();
1033 let topic2: Topic = [0x02; 32].into();
1034 let topic3: Topic = [0x03; 32].into();
1035
1036 statement.set_topic(0, topic1);
1037 statement.set_topic(1, topic2);
1038
1039 let filter_any = crate::OptimizedTopicFilter::Any;
1040 assert!(filter_any.matches(&statement));
1041
1042 let filter_all =
1043 crate::OptimizedTopicFilter::MatchAll([topic1, topic2].iter().cloned().collect());
1044 assert!(filter_all.matches(&statement));
1045
1046 let filter_all_fail =
1047 crate::OptimizedTopicFilter::MatchAll([topic1, topic3].iter().cloned().collect());
1048 assert!(!filter_all_fail.matches(&statement));
1049
1050 let filter_any_match =
1051 crate::OptimizedTopicFilter::MatchAny([topic2, topic3].iter().cloned().collect());
1052 assert!(filter_any_match.matches(&statement));
1053
1054 let filter_any_fail =
1055 crate::OptimizedTopicFilter::MatchAny([topic3].iter().cloned().collect());
1056 assert!(!filter_any_fail.matches(&statement));
1057 }
1058
1059 #[test]
1060 fn statement_type_info_matches_encoding() {
1061 let statement_type = Statement::type_info();
1064 let vec_field_meta = MetaType::new::<Vec<Field>>();
1065
1066 match statement_type.type_def {
1068 scale_info::TypeDef::Composite(composite) => {
1069 assert_eq!(composite.fields.len(), 1, "Statement should have exactly one field");
1070 let field = &composite.fields[0];
1071 assert!(field.name.is_none(), "Field should be unnamed (newtype pattern)");
1072 assert_eq!(field.ty, vec_field_meta, "Statement's inner type should be Vec<Field>");
1073 },
1074 _ => panic!("Statement TypeInfo should be a Composite"),
1075 }
1076 }
1077
1078 #[test]
1079 fn measure_hash_30_000_statements() {
1080 use std::time::Instant;
1081 const NUM_STATEMENTS: usize = 30_000;
1082 let (keyring, _) = sr25519::Pair::generate();
1083
1084 let statements: Vec<Statement> = (0..NUM_STATEMENTS)
1086 .map(|i| {
1087 let mut statement = Statement::new();
1088
1089 statement.set_expiry(i as u64);
1090 statement.set_topic(0, [(i % 256) as u8; 32].into());
1091 statement.set_plain_data(vec![i as u8; 512]);
1092 statement.sign_sr25519_private(&keyring);
1093
1094 statement.sign_sr25519_private(&keyring);
1095 statement
1096 })
1097 .collect();
1098 let start = Instant::now();
1100 let hashes: Vec<[u8; 32]> = statements.iter().map(|s| s.hash()).collect();
1101 let elapsed = start.elapsed();
1102 println!("Time to hash {} statements: {:?}", NUM_STATEMENTS, elapsed);
1103 println!("Average time per statement: {:?}", elapsed / NUM_STATEMENTS as u32);
1104 let unique_hashes: std::collections::HashSet<_> = hashes.iter().collect();
1106 assert_eq!(unique_hashes.len(), NUM_STATEMENTS);
1107 }
1108
1109 #[test]
1110 fn estimated_encoded_size_is_sufficient() {
1111 const MAX_ACCEPTED_OVERHEAD: usize = 33;
1113
1114 let proof = Proof::Secp256k1Ecdsa { signature: [42u8; 65], signer: [24u8; 33] };
1116 let decryption_key = [0xde; 32];
1117 let data = vec![55; 1000];
1118 let expiry = 999;
1119 let channel = [0xcc; 32];
1120
1121 let mut statement = Statement::new();
1123 statement.set_proof(proof);
1124 statement.set_decryption_key(decryption_key);
1125 statement.set_expiry(expiry);
1126 statement.set_channel(channel);
1127 for i in 0..MAX_TOPICS {
1128 statement.set_topic(i, [i as u8; 32].into());
1129 }
1130 statement.set_plain_data(data);
1131
1132 let encoded = statement.encode();
1133 let estimated = statement.estimated_encoded_size(false);
1134 assert!(
1135 estimated >= encoded.len(),
1136 "estimated_encoded_size ({}) should be >= actual encoded length ({})",
1137 estimated,
1138 encoded.len()
1139 );
1140 let overhead = estimated - encoded.len();
1141 assert!(
1142 overhead <= MAX_ACCEPTED_OVERHEAD,
1143 "estimated overhead ({}) should be small, estimated: {}, actual: {}",
1144 overhead,
1145 estimated,
1146 encoded.len()
1147 );
1148
1149 let signing_payload = statement.encoded(true);
1151 let signing_estimated = statement.estimated_encoded_size(true);
1152 assert!(
1153 signing_estimated >= signing_payload.len(),
1154 "estimated_encoded_size for signing ({}) should be >= actual signing payload length ({})",
1155 signing_estimated,
1156 signing_payload.len()
1157 );
1158 let signing_overhead = signing_estimated - signing_payload.len();
1159 assert!(
1160 signing_overhead <= MAX_ACCEPTED_OVERHEAD,
1161 "signing overhead ({}) should be small, estimated: {}, actual: {}",
1162 signing_overhead,
1163 signing_estimated,
1164 signing_payload.len()
1165 );
1166
1167 let empty_statement = Statement::new();
1169 let empty_encoded = empty_statement.encode();
1170 let empty_estimated = empty_statement.estimated_encoded_size(false);
1171 assert!(
1172 empty_estimated >= empty_encoded.len(),
1173 "estimated_encoded_size for empty ({}) should be >= actual encoded length ({})",
1174 empty_estimated,
1175 empty_encoded.len()
1176 );
1177 let empty_overhead = empty_estimated - empty_encoded.len();
1178 assert!(
1179 empty_overhead <= MAX_ACCEPTED_OVERHEAD,
1180 "empty overhead ({}) should be minimal, estimated: {}, actual: {}",
1181 empty_overhead,
1182 empty_estimated,
1183 empty_encoded.len()
1184 );
1185 }
1186
1187 fn populate_canonical_fixture(stmt: &mut Statement) {
1196 stmt.set_topic(0, [0x01; 32].into());
1197 stmt.set_topic(1, [0x02; 32].into());
1198 stmt.set_topic(2, [0x03; 32].into());
1199 stmt.set_channel([0xcc; 32]);
1200 stmt.set_expiry_from_parts(0x7fff_ffff, 0xabcd_1234);
1201 stmt.set_plain_data(vec![0xde, 0xad, 0xbe, 0xef]);
1202 }
1203
1204 fn canonical_tail() -> Vec<u8> {
1208 let mut v = Vec::new();
1209 v.push(0x02); v.extend_from_slice(&[0x34, 0x12, 0xcd, 0xab, 0xff, 0xff, 0xff, 0x7f]); v.push(0x03); v.extend_from_slice(&[0xcc; 32]);
1213 v.push(0x04); v.extend_from_slice(&[0x01; 32]);
1215 v.push(0x05); v.extend_from_slice(&[0x02; 32]);
1217 v.push(0x06); v.extend_from_slice(&[0x03; 32]);
1219 v.push(0x08); v.push(0x10); v.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
1222 v
1223 }
1224
1225 #[test]
1227 fn wire_format_sr25519_pinned() {
1228 let mut stmt = Statement::new();
1229 populate_canonical_fixture(&mut stmt);
1230 stmt.set_proof(Proof::Sr25519 { signature: [0x11; 64], signer: [0xAA; 32] });
1231
1232 let mut expected = Vec::new();
1233 expected.push(0x1c); expected.push(0x00); expected.push(0x00); expected.extend_from_slice(&[0x11; 64]); expected.extend_from_slice(&[0xAA; 32]); expected.extend(canonical_tail());
1239
1240 assert_eq!(stmt.encode(), expected, "Sr25519 wire format drifted");
1241 assert_eq!(expected.len(), 246);
1242 assert_eq!(Statement::decode(&mut expected.as_slice()).unwrap(), stmt);
1244 }
1245
1246 #[test]
1248 fn wire_format_legacy_onchain_proof_is_rejected() {
1249 let mut legacy = Vec::new();
1251 legacy.push(0x08); legacy.push(0x00); legacy.push(0x03); legacy.extend_from_slice(&[0xdd; 32]); legacy.extend_from_slice(&[0xee; 32]); legacy.extend_from_slice(&[0xbe, 0xba, 0xfe, 0xca, 0xef, 0xbe, 0xad, 0xde]); legacy.push(0x02); legacy.extend_from_slice(&[0x2a, 0, 0, 0, 0, 0, 0, 0]); assert!(
1261 Statement::decode(&mut legacy.as_slice()).is_err(),
1262 "legacy OnChain bytes must no longer decode into a Statement",
1263 );
1264
1265 let mut survivor = Vec::new();
1269 survivor.push(0x08);
1270 survivor.push(0x00);
1271 survivor.push(0x00); survivor.extend_from_slice(&[0xdd; 64]);
1273 survivor.extend_from_slice(&[0xee; 32]);
1274 survivor.push(0x02);
1275 survivor.extend_from_slice(&[0x2a, 0, 0, 0, 0, 0, 0, 0]);
1276 assert!(
1277 Statement::decode(&mut survivor.as_slice()).is_ok(),
1278 "surviving variant in the same byte layout must still decode",
1279 );
1280 }
1281
1282 #[test]
1284 fn proof_max_encoded_len_after_onchain_removal() {
1285 assert_eq!(
1286 Proof::max_encoded_len(),
1287 1 + 65 + 33,
1288 "max_encoded_len must equal Secp256k1Ecdsa's payload + 1-byte discriminant",
1289 );
1290 }
1291}