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}
281
282impl Proof {
283 pub fn account_id(&self) -> AccountId {
285 match self {
286 Proof::Sr25519 { signer, .. } => *signer,
287 Proof::Ed25519 { signer, .. } => *signer,
288 Proof::Secp256k1Ecdsa { signer, .. } => {
289 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer).into()
290 },
291 }
292 }
293}
294
295#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
298#[repr(u8)]
299pub enum Field {
300 AuthenticityProof(Proof) = 0,
302 DecryptionKey(DecryptionKey) = 1,
304 Expiry(u64) = 2,
306 Channel(Channel) = 3,
308 Topic1(Topic) = 4,
310 Topic2(Topic) = 5,
312 Topic3(Topic) = 6,
314 Topic4(Topic) = 7,
316 Data(Vec<u8>) = 8,
318}
319
320impl Field {
321 fn discriminant(&self) -> u8 {
322 unsafe { *(self as *const Self as *const u8) }
325 }
326}
327
328#[derive(DecodeWithMemTracking, Debug, Clone, PartialEq, Eq, Default)]
330pub struct Statement {
331 proof: Option<Proof>,
333 #[deprecated(note = "Experimental feature, may be removed/changed in future releases")]
335 decryption_key: Option<DecryptionKey>,
336 channel: Option<Channel>,
347 expiry: u64,
362 num_topics: u8,
364 topics: [Topic; MAX_TOPICS],
366 data: Option<Vec<u8>>,
368}
369
370impl TypeInfo for Statement {
373 type Identity = Self;
374
375 fn type_info() -> Type {
376 Type::builder()
378 .path(Path::new("Statement", module_path!()))
379 .docs(&["Statement structure"])
380 .composite(Fields::unnamed().field(|f| f.ty::<Vec<Field>>()))
381 }
382}
383
384impl Decode for Statement {
385 fn decode<I: codec::Input>(input: &mut I) -> core::result::Result<Self, codec::Error> {
386 let num_fields: codec::Compact<u32> = Decode::decode(input)?;
389 let mut tag = 0;
390 let mut statement = Statement::new();
391 for i in 0..num_fields.into() {
392 let field: Field = Decode::decode(input)?;
393 if i > 0 && field.discriminant() <= tag {
394 return Err("Invalid field order or duplicate fields".into());
395 }
396 tag = field.discriminant();
397 match field {
398 Field::AuthenticityProof(p) => statement.set_proof(p),
399 Field::DecryptionKey(key) => statement.set_decryption_key(key),
400 Field::Expiry(p) => statement.set_expiry(p),
401 Field::Channel(c) => statement.set_channel(c),
402 Field::Topic1(t) => statement.set_topic(0, t),
403 Field::Topic2(t) => statement.set_topic(1, t),
404 Field::Topic3(t) => statement.set_topic(2, t),
405 Field::Topic4(t) => statement.set_topic(3, t),
406 Field::Data(data) => statement.set_plain_data(data),
407 }
408 }
409 Ok(statement)
410 }
411}
412
413impl Encode for Statement {
414 fn encode(&self) -> Vec<u8> {
415 self.encoded(false)
416 }
417}
418
419#[derive(Clone, Copy, PartialEq, Eq, Debug)]
420pub enum SignatureVerificationResult {
422 Valid(AccountId),
424 Invalid,
426 NoSignature,
428}
429
430impl Statement {
431 pub fn new() -> Statement {
433 Default::default()
434 }
435
436 pub fn new_with_proof(proof: Proof) -> Statement {
438 let mut statement = Self::new();
439 statement.set_proof(proof);
440 statement
441 }
442
443 pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool {
449 let to_sign = self.signature_material();
450 if let Some(signature) = key.sign(&to_sign) {
451 let proof = Proof::Sr25519 {
452 signature: signature.into_inner().into(),
453 signer: key.clone().into_inner().into(),
454 };
455 self.set_proof(proof);
456 true
457 } else {
458 false
459 }
460 }
461
462 pub fn topics(&self) -> &[Topic] {
464 &self.topics[..self.num_topics as usize]
465 }
466
467 #[cfg(feature = "std")]
469 pub fn sign_sr25519_private(&mut self, key: &sp_core::sr25519::Pair) {
470 let to_sign = self.signature_material();
471 let proof =
472 Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
473 self.set_proof(proof);
474 }
475
476 pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool {
482 let to_sign = self.signature_material();
483 if let Some(signature) = key.sign(&to_sign) {
484 let proof = Proof::Ed25519 {
485 signature: signature.into_inner().into(),
486 signer: key.clone().into_inner().into(),
487 };
488 self.set_proof(proof);
489 true
490 } else {
491 false
492 }
493 }
494
495 #[cfg(feature = "std")]
497 pub fn sign_ed25519_private(&mut self, key: &sp_core::ed25519::Pair) {
498 let to_sign = self.signature_material();
499 let proof =
500 Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
501 self.set_proof(proof);
502 }
503
504 pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool {
514 let to_sign = self.signature_material();
515 if let Some(signature) = key.sign(&to_sign) {
516 let proof = Proof::Secp256k1Ecdsa {
517 signature: signature.into_inner().into(),
518 signer: key.clone().into_inner().0,
519 };
520 self.set_proof(proof);
521 true
522 } else {
523 false
524 }
525 }
526
527 #[cfg(feature = "std")]
529 pub fn sign_ecdsa_private(&mut self, key: &sp_core::ecdsa::Pair) {
530 let to_sign = self.signature_material();
531 let proof =
532 Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 };
533 self.set_proof(proof);
534 }
535
536 pub fn verify_signature(&self) -> SignatureVerificationResult {
538 use sp_runtime::traits::Verify;
539
540 match self.proof() {
541 None => SignatureVerificationResult::NoSignature,
542 Some(Proof::Sr25519 { signature, signer }) => {
543 let to_sign = self.signature_material();
544 let signature = sp_core::sr25519::Signature::from(*signature);
545 let public = sp_core::sr25519::Public::from(*signer);
546 if signature.verify(to_sign.as_slice(), &public) {
547 SignatureVerificationResult::Valid(*signer)
548 } else {
549 SignatureVerificationResult::Invalid
550 }
551 },
552 Some(Proof::Ed25519 { signature, signer }) => {
553 let to_sign = self.signature_material();
554 let signature = sp_core::ed25519::Signature::from(*signature);
555 let public = sp_core::ed25519::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::Secp256k1Ecdsa { signature, signer }) => {
563 let to_sign = self.signature_material();
564 let signature = sp_core::ecdsa::Signature::from(*signature);
565 let public = sp_core::ecdsa::Public::from(*signer);
566 if signature.verify(to_sign.as_slice(), &public) {
567 let sender_hash =
568 <sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer);
569 SignatureVerificationResult::Valid(sender_hash.into())
570 } else {
571 SignatureVerificationResult::Invalid
572 }
573 },
574 }
575 }
576
577 #[cfg(feature = "std")]
579 pub fn hash(&self) -> [u8; 32] {
580 self.using_encoded(hash_encoded)
581 }
582
583 pub fn topic(&self, index: usize) -> Option<Topic> {
585 if index < self.num_topics as usize {
586 Some(self.topics[index])
587 } else {
588 None
589 }
590 }
591
592 #[allow(deprecated)]
594 pub fn decryption_key(&self) -> Option<DecryptionKey> {
595 self.decryption_key
596 }
597
598 pub fn into_data(self) -> Option<Vec<u8>> {
600 self.data
601 }
602
603 pub fn proof(&self) -> Option<&Proof> {
605 self.proof.as_ref()
606 }
607
608 pub fn account_id(&self) -> Option<AccountId> {
610 self.proof.as_ref().map(Proof::account_id)
611 }
612
613 pub fn data(&self) -> Option<&Vec<u8>> {
615 self.data.as_ref()
616 }
617
618 pub fn data_len(&self) -> usize {
620 self.data().map_or(0, Vec::len)
621 }
622
623 pub fn channel(&self) -> Option<Channel> {
625 self.channel
626 }
627
628 pub fn expiry(&self) -> u64 {
630 self.expiry
631 }
632
633 pub fn get_expiration_timestamp_secs(&self) -> u32 {
638 (self.expiry >> 32) as u32
639 }
640
641 fn signature_material(&self) -> Vec<u8> {
643 self.encoded(true)
644 }
645
646 pub fn remove_proof(&mut self) {
648 self.proof = None;
649 }
650
651 pub fn set_proof(&mut self, proof: Proof) {
653 self.proof = Some(proof)
654 }
655
656 pub fn set_expiry(&mut self, expiry: u64) {
658 self.expiry = expiry;
659 }
660
661 pub fn set_expiry_from_parts(&mut self, expiration_timestamp_secs: u32, sequence_number: u32) {
663 self.expiry = (expiration_timestamp_secs as u64) << 32 | sequence_number as u64;
664 }
665
666 pub fn set_channel(&mut self, channel: Channel) {
668 self.channel = Some(channel)
669 }
670
671 pub fn set_topic(&mut self, index: usize, topic: Topic) {
673 if index < MAX_TOPICS {
674 self.topics[index] = topic;
675 self.num_topics = self.num_topics.max(index as u8 + 1);
676 }
677 }
678
679 #[allow(deprecated)]
681 pub fn set_decryption_key(&mut self, key: DecryptionKey) {
682 self.decryption_key = Some(key);
683 }
684
685 pub fn set_plain_data(&mut self, data: Vec<u8>) {
687 self.data = Some(data)
688 }
689
690 #[allow(deprecated)]
702 fn estimated_encoded_size(&self, for_signing: bool) -> usize {
703 let proof_size =
704 if !for_signing && self.proof.is_some() { 1 + Proof::max_encoded_len() } else { 0 };
705 let decryption_key_size =
706 if self.decryption_key.is_some() { 1 + DecryptionKey::max_encoded_len() } else { 0 };
707 let expiry_size = 1 + u64::max_encoded_len();
708 let channel_size = if self.channel.is_some() { 1 + Channel::max_encoded_len() } else { 0 };
709 let topics_size = self.num_topics as usize * (1 + Topic::max_encoded_len());
710 let data_size = self
711 .data
712 .as_ref()
713 .map_or(0, |d| 1 + Compact::<u32>::max_encoded_len() + d.len());
714 let compact_prefix_size = if !for_signing { Compact::<u32>::max_encoded_len() } else { 0 };
715
716 compact_prefix_size +
717 proof_size +
718 decryption_key_size +
719 expiry_size +
720 channel_size +
721 topics_size +
722 data_size
723 }
724
725 #[allow(deprecated)]
726 fn encoded(&self, for_signing: bool) -> Vec<u8> {
727 let num_fields = if !for_signing && self.proof.is_some() { 2 } else { 1 } +
731 if self.decryption_key.is_some() { 1 } else { 0 } +
732 if self.channel.is_some() { 1 } else { 0 } +
733 if self.data.is_some() { 1 } else { 0 } +
734 self.num_topics as u32;
735
736 let mut output = Vec::with_capacity(self.estimated_encoded_size(for_signing));
737 if !for_signing {
741 let compact_len = codec::Compact::<u32>(num_fields);
742 compact_len.encode_to(&mut output);
743
744 if let Some(proof) = &self.proof {
745 0u8.encode_to(&mut output);
746 proof.encode_to(&mut output);
747 }
748 }
749 if let Some(decryption_key) = &self.decryption_key {
750 1u8.encode_to(&mut output);
751 decryption_key.encode_to(&mut output);
752 }
753
754 2u8.encode_to(&mut output);
755 self.expiry().encode_to(&mut output);
756
757 if let Some(channel) = &self.channel {
758 3u8.encode_to(&mut output);
759 channel.encode_to(&mut output);
760 }
761 for t in 0..self.num_topics {
762 (4u8 + t).encode_to(&mut output);
763 self.topics[t as usize].encode_to(&mut output);
764 }
765 if let Some(data) = &self.data {
766 8u8.encode_to(&mut output);
767 data.encode_to(&mut output);
768 }
769 output
770 }
771
772 #[allow(deprecated)]
774 #[cfg(feature = "std")]
775 pub fn encrypt(
776 &mut self,
777 data: &[u8],
778 key: &sp_core::ed25519::Public,
779 ) -> core::result::Result<(), ecies::Error> {
780 let encrypted = ecies::encrypt_ed25519(key, data)?;
781 self.data = Some(encrypted);
782 self.decryption_key = Some((*key).into());
783 Ok(())
784 }
785
786 #[cfg(feature = "std")]
788 pub fn decrypt_private(
789 &self,
790 key: &sp_core::ed25519::Pair,
791 ) -> core::result::Result<Option<Vec<u8>>, ecies::Error> {
792 self.data.as_ref().map(|d| ecies::decrypt_ed25519(key, d)).transpose()
793 }
794}
795
796#[cfg(test)]
797mod test {
798 use crate::{
799 hash_encoded, Field, Proof, SignatureVerificationResult, Statement, Topic, MAX_TOPICS,
800 };
801 use codec::{Decode, Encode, MaxEncodedLen};
802 use scale_info::{MetaType, TypeInfo};
803 use sp_application_crypto::Pair;
804 use sp_core::sr25519;
805
806 #[test]
807 fn statement_encoding_matches_vec() {
808 let mut statement = Statement::new();
809 assert!(statement.proof().is_none());
810 let proof = Proof::Sr25519 { signature: [42u8; 64], signer: [24u8; 32] };
811
812 let decryption_key = [0xde; 32];
813 let topic1: Topic = [0x01; 32].into();
814 let topic2: Topic = [0x02; 32].into();
815 let data = vec![55, 99];
816 let expiry = 999;
817 let channel = [0xcc; 32];
818
819 statement.set_proof(proof.clone());
820 statement.set_decryption_key(decryption_key);
821 statement.set_expiry(expiry);
822 statement.set_channel(channel);
823 statement.set_topic(0, topic1);
824 statement.set_topic(1, topic2);
825 statement.set_plain_data(data.clone());
826
827 statement.set_topic(5, [0x55; 32].into());
828 assert_eq!(statement.topic(5), None);
829
830 let fields = vec![
831 Field::AuthenticityProof(proof.clone()),
832 Field::DecryptionKey(decryption_key),
833 Field::Expiry(expiry),
834 Field::Channel(channel),
835 Field::Topic1(topic1),
836 Field::Topic2(topic2),
837 Field::Data(data.clone()),
838 ];
839
840 let encoded = statement.encode();
841 assert_eq!(statement.hash(), hash_encoded(&encoded));
842 assert_eq!(encoded, fields.encode());
843
844 let decoded = Statement::decode(&mut encoded.as_slice()).unwrap();
845 assert_eq!(decoded, statement);
846 }
847
848 #[test]
849 fn decode_checks_fields() {
850 let topic1: Topic = [0x01; 32].into();
851 let topic2: Topic = [0x02; 32].into();
852 let priority = 999;
853
854 let dup_topic1 = vec![
855 Field::Expiry(priority),
856 Field::Topic1(topic1),
857 Field::Topic1(topic1),
858 Field::Topic2(topic2),
859 ]
860 .encode();
861 assert!(Statement::decode(&mut dup_topic1.as_slice()).is_err());
862
863 let topic1_before_expiry =
864 vec![Field::Topic1(topic1), Field::Expiry(priority), Field::Topic2(topic2)].encode();
865 assert!(Statement::decode(&mut topic1_before_expiry.as_slice()).is_err());
866
867 let dup_expiry = vec![Field::Expiry(1), Field::Expiry(2)].encode();
868 assert!(Statement::decode(&mut dup_expiry.as_slice()).is_err());
869
870 let dup_data = vec![Field::Data(vec![1]), Field::Data(vec![2])].encode();
871 assert!(Statement::decode(&mut dup_data.as_slice()).is_err());
872
873 let data_before_expiry = vec![Field::Data(vec![1]), Field::Expiry(42)].encode();
874 assert!(Statement::decode(&mut data_before_expiry.as_slice()).is_err());
875
876 let channel_before_expiry = vec![Field::Channel([0; 32]), Field::Expiry(1)].encode();
877 assert!(Statement::decode(&mut channel_before_expiry.as_slice()).is_err());
878
879 let topic2_before_topic1 =
880 vec![Field::Expiry(1), Field::Topic2(topic1), Field::Topic1(topic2)].encode();
881 assert!(Statement::decode(&mut topic2_before_topic1.as_slice()).is_err());
882 }
883
884 #[test]
885 fn decode_rejects_malformed_bytes() {
886 assert!(Statement::decode(&mut &[][..]).is_err());
887
888 let valid = vec![Field::Expiry(42)].encode();
890 let decoded = Statement::decode(&mut valid.as_slice()).unwrap();
891 assert_eq!(decoded.expiry(), 42);
892
893 assert!(Statement::decode(&mut &valid[..1][..]).is_err());
895
896 let mut invalid_discriminant = valid.clone();
898 invalid_discriminant[1] = 9;
899 assert!(Statement::decode(&mut invalid_discriminant.as_slice()).is_err());
900
901 invalid_discriminant[1] = 255;
902 assert!(Statement::decode(&mut invalid_discriminant.as_slice()).is_err());
903
904 assert!(Statement::decode(&mut &valid[..5][..]).is_err());
906
907 let with_proof = vec![
909 Field::AuthenticityProof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] }),
910 Field::Expiry(42),
911 ]
912 .encode();
913 assert!(Statement::decode(&mut with_proof.as_slice()).is_ok());
914
915 let mut invalid_proof_variant = with_proof.clone();
916 invalid_proof_variant[2] = 99;
917 assert!(Statement::decode(&mut invalid_proof_variant.as_slice()).is_err());
918
919 assert!(Statement::decode(&mut &with_proof[..6][..]).is_err());
921
922 let mut inflated_count = valid.clone();
924 inflated_count[0] = 5 << 2; assert!(Statement::decode(&mut inflated_count.as_slice()).is_err());
926 }
927
928 #[test]
929 fn sign_and_verify() {
930 let mut statement = Statement::new();
931 statement.set_plain_data(vec![42]);
932
933 let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
934 let ed25519_kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap();
935 let secp256k1_kp = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
936
937 statement.sign_sr25519_private(&sr25519_kp);
938 assert_eq!(
939 statement.verify_signature(),
940 SignatureVerificationResult::Valid(sr25519_kp.public().0)
941 );
942
943 statement.sign_ed25519_private(&ed25519_kp);
944 assert_eq!(
945 statement.verify_signature(),
946 SignatureVerificationResult::Valid(ed25519_kp.public().0)
947 );
948
949 statement.sign_ecdsa_private(&secp256k1_kp);
950 assert_eq!(
951 statement.verify_signature(),
952 SignatureVerificationResult::Valid(sp_crypto_hashing::blake2_256(
953 &secp256k1_kp.public().0
954 ))
955 );
956
957 statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
959 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
960
961 statement.set_proof(Proof::Ed25519 { signature: [0xAB; 64], signer: [0xCD; 32] });
963 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
964
965 statement.set_proof(Proof::Secp256k1Ecdsa { signature: [0u8; 65], signer: [0u8; 33] });
967 assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
968
969 statement.remove_proof();
970 assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature);
971 }
972
973 #[test]
974 fn encrypt_decrypt() {
975 let mut statement = Statement::new();
976 let (pair, _) = sp_core::ed25519::Pair::generate();
977 let plain = b"test data".to_vec();
978
979 statement.encrypt(&plain, &pair.public()).unwrap();
981 assert_ne!(plain.as_slice(), statement.data().unwrap().as_slice());
982
983 let decrypted = statement.decrypt_private(&pair).unwrap();
984 assert_eq!(decrypted, Some(plain));
985 }
986
987 #[test]
988 fn check_matches() {
989 let mut statement = Statement::new();
990 let topic1: Topic = [0x01; 32].into();
991 let topic2: Topic = [0x02; 32].into();
992 let topic3: Topic = [0x03; 32].into();
993
994 statement.set_topic(0, topic1);
995 statement.set_topic(1, topic2);
996
997 let filter_any = crate::OptimizedTopicFilter::Any;
998 assert!(filter_any.matches(&statement));
999
1000 let filter_all =
1001 crate::OptimizedTopicFilter::MatchAll([topic1, topic2].iter().cloned().collect());
1002 assert!(filter_all.matches(&statement));
1003
1004 let filter_all_fail =
1005 crate::OptimizedTopicFilter::MatchAll([topic1, topic3].iter().cloned().collect());
1006 assert!(!filter_all_fail.matches(&statement));
1007
1008 let filter_any_match =
1009 crate::OptimizedTopicFilter::MatchAny([topic2, topic3].iter().cloned().collect());
1010 assert!(filter_any_match.matches(&statement));
1011
1012 let filter_any_fail =
1013 crate::OptimizedTopicFilter::MatchAny([topic3].iter().cloned().collect());
1014 assert!(!filter_any_fail.matches(&statement));
1015 }
1016
1017 #[test]
1018 fn statement_type_info_matches_encoding() {
1019 let statement_type = Statement::type_info();
1022 let vec_field_meta = MetaType::new::<Vec<Field>>();
1023
1024 match statement_type.type_def {
1026 scale_info::TypeDef::Composite(composite) => {
1027 assert_eq!(composite.fields.len(), 1, "Statement should have exactly one field");
1028 let field = &composite.fields[0];
1029 assert!(field.name.is_none(), "Field should be unnamed (newtype pattern)");
1030 assert_eq!(field.ty, vec_field_meta, "Statement's inner type should be Vec<Field>");
1031 },
1032 _ => panic!("Statement TypeInfo should be a Composite"),
1033 }
1034 }
1035
1036 #[test]
1037 fn measure_hash_30_000_statements() {
1038 use std::time::Instant;
1039 const NUM_STATEMENTS: usize = 30_000;
1040 let (keyring, _) = sr25519::Pair::generate();
1041
1042 let statements: Vec<Statement> = (0..NUM_STATEMENTS)
1044 .map(|i| {
1045 let mut statement = Statement::new();
1046
1047 statement.set_expiry(i as u64);
1048 statement.set_topic(0, [(i % 256) as u8; 32].into());
1049 statement.set_plain_data(vec![i as u8; 512]);
1050 statement.sign_sr25519_private(&keyring);
1051
1052 statement.sign_sr25519_private(&keyring);
1053 statement
1054 })
1055 .collect();
1056 let start = Instant::now();
1058 let hashes: Vec<[u8; 32]> = statements.iter().map(|s| s.hash()).collect();
1059 let elapsed = start.elapsed();
1060 println!("Time to hash {} statements: {:?}", NUM_STATEMENTS, elapsed);
1061 println!("Average time per statement: {:?}", elapsed / NUM_STATEMENTS as u32);
1062 let unique_hashes: std::collections::HashSet<_> = hashes.iter().collect();
1064 assert_eq!(unique_hashes.len(), NUM_STATEMENTS);
1065 }
1066
1067 #[test]
1068 fn estimated_encoded_size_is_sufficient() {
1069 const MAX_ACCEPTED_OVERHEAD: usize = 33;
1071
1072 let proof = Proof::Secp256k1Ecdsa { signature: [42u8; 65], signer: [24u8; 33] };
1074 let decryption_key = [0xde; 32];
1075 let data = vec![55; 1000];
1076 let expiry = 999;
1077 let channel = [0xcc; 32];
1078
1079 let mut statement = Statement::new();
1081 statement.set_proof(proof);
1082 statement.set_decryption_key(decryption_key);
1083 statement.set_expiry(expiry);
1084 statement.set_channel(channel);
1085 for i in 0..MAX_TOPICS {
1086 statement.set_topic(i, [i as u8; 32].into());
1087 }
1088 statement.set_plain_data(data);
1089
1090 let encoded = statement.encode();
1091 let estimated = statement.estimated_encoded_size(false);
1092 assert!(
1093 estimated >= encoded.len(),
1094 "estimated_encoded_size ({}) should be >= actual encoded length ({})",
1095 estimated,
1096 encoded.len()
1097 );
1098 let overhead = estimated - encoded.len();
1099 assert!(
1100 overhead <= MAX_ACCEPTED_OVERHEAD,
1101 "estimated overhead ({}) should be small, estimated: {}, actual: {}",
1102 overhead,
1103 estimated,
1104 encoded.len()
1105 );
1106
1107 let signing_payload = statement.encoded(true);
1109 let signing_estimated = statement.estimated_encoded_size(true);
1110 assert!(
1111 signing_estimated >= signing_payload.len(),
1112 "estimated_encoded_size for signing ({}) should be >= actual signing payload length ({})",
1113 signing_estimated,
1114 signing_payload.len()
1115 );
1116 let signing_overhead = signing_estimated - signing_payload.len();
1117 assert!(
1118 signing_overhead <= MAX_ACCEPTED_OVERHEAD,
1119 "signing overhead ({}) should be small, estimated: {}, actual: {}",
1120 signing_overhead,
1121 signing_estimated,
1122 signing_payload.len()
1123 );
1124
1125 let empty_statement = Statement::new();
1127 let empty_encoded = empty_statement.encode();
1128 let empty_estimated = empty_statement.estimated_encoded_size(false);
1129 assert!(
1130 empty_estimated >= empty_encoded.len(),
1131 "estimated_encoded_size for empty ({}) should be >= actual encoded length ({})",
1132 empty_estimated,
1133 empty_encoded.len()
1134 );
1135 let empty_overhead = empty_estimated - empty_encoded.len();
1136 assert!(
1137 empty_overhead <= MAX_ACCEPTED_OVERHEAD,
1138 "empty overhead ({}) should be minimal, estimated: {}, actual: {}",
1139 empty_overhead,
1140 empty_estimated,
1141 empty_encoded.len()
1142 );
1143 }
1144
1145 fn populate_canonical_fixture(stmt: &mut Statement) {
1154 stmt.set_topic(0, [0x01; 32].into());
1155 stmt.set_topic(1, [0x02; 32].into());
1156 stmt.set_topic(2, [0x03; 32].into());
1157 stmt.set_channel([0xcc; 32]);
1158 stmt.set_expiry_from_parts(0x7fff_ffff, 0xabcd_1234);
1159 stmt.set_plain_data(vec![0xde, 0xad, 0xbe, 0xef]);
1160 }
1161
1162 fn canonical_tail() -> Vec<u8> {
1166 let mut v = Vec::new();
1167 v.push(0x02); v.extend_from_slice(&[0x34, 0x12, 0xcd, 0xab, 0xff, 0xff, 0xff, 0x7f]); v.push(0x03); v.extend_from_slice(&[0xcc; 32]);
1171 v.push(0x04); v.extend_from_slice(&[0x01; 32]);
1173 v.push(0x05); v.extend_from_slice(&[0x02; 32]);
1175 v.push(0x06); v.extend_from_slice(&[0x03; 32]);
1177 v.push(0x08); v.push(0x10); v.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
1180 v
1181 }
1182
1183 #[test]
1185 fn wire_format_sr25519_pinned() {
1186 let mut stmt = Statement::new();
1187 populate_canonical_fixture(&mut stmt);
1188 stmt.set_proof(Proof::Sr25519 { signature: [0x11; 64], signer: [0xAA; 32] });
1189
1190 let mut expected = Vec::new();
1191 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());
1197
1198 assert_eq!(stmt.encode(), expected, "Sr25519 wire format drifted");
1199 assert_eq!(expected.len(), 246);
1200 assert_eq!(Statement::decode(&mut expected.as_slice()).unwrap(), stmt);
1202 }
1203
1204 #[test]
1206 fn wire_format_legacy_onchain_proof_is_rejected() {
1207 let mut legacy = Vec::new();
1209 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!(
1219 Statement::decode(&mut legacy.as_slice()).is_err(),
1220 "legacy OnChain bytes must no longer decode into a Statement",
1221 );
1222
1223 let mut survivor = Vec::new();
1227 survivor.push(0x08);
1228 survivor.push(0x00);
1229 survivor.push(0x00); survivor.extend_from_slice(&[0xdd; 64]);
1231 survivor.extend_from_slice(&[0xee; 32]);
1232 survivor.push(0x02);
1233 survivor.extend_from_slice(&[0x2a, 0, 0, 0, 0, 0, 0, 0]);
1234 assert!(
1235 Statement::decode(&mut survivor.as_slice()).is_ok(),
1236 "surviving variant in the same byte layout must still decode",
1237 );
1238 }
1239
1240 #[test]
1242 fn proof_max_encoded_len_after_onchain_removal() {
1243 assert_eq!(
1244 Proof::max_encoded_len(),
1245 1 + 65 + 33,
1246 "max_encoded_len must equal Secp256k1Ecdsa's payload + 1-byte discriminant",
1247 );
1248 }
1249}