referrerpolicy=no-referrer-when-downgrade

sp_statement_store/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(missing_docs)]
20
21//! A crate which contains statement-store primitives.
22
23extern 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/// Statement topic.
34///
35/// A 32-byte topic identifier that serializes as a hex string (like `sp_core::Bytes`).
36#[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
111/// Decryption key identifier.
112pub type DecryptionKey = [u8; 32];
113/// Statement hash.
114pub type Hash = [u8; 32];
115/// Block hash.
116pub type BlockHash = [u8; 32];
117/// Account id
118pub type AccountId = [u8; 32];
119/// Statement channel.
120pub type Channel = [u8; 32];
121
122/// Total number of topic fields allowed in a statement and in `MatchAll` filters.
123pub const MAX_TOPICS: usize = 4;
124/// `MatchAny` allows to provide a list of topics match against. This is the maximum number of
125/// topics allowed.
126pub const MAX_ANY_TOPICS: usize = 128;
127
128/// Statement allowance limits for an account.
129#[derive(Clone, Default, PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
130pub struct StatementAllowance {
131	/// Maximum number of statements allowed
132	pub max_count: u32,
133	/// Maximum total size of statements in bytes
134	pub max_size: u32,
135}
136
137impl StatementAllowance {
138	/// Create a new statement allowance.
139	pub fn new(max_count: u32, max_size: u32) -> Self {
140		Self { max_count, max_size }
141	}
142
143	/// Saturating addition of statement allowances.
144	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	/// Saturating subtraction of statement allowances.
152	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	/// Check if the statement allowance is depleted.
160	pub fn is_depleted(&self) -> bool {
161		self.max_count == 0 || self.max_size == 0
162	}
163}
164
165/// Storage key prefix for per-account statement allowances.
166pub const STATEMENT_ALLOWANCE_PREFIX: &[u8] = b":statement_allowance:";
167
168/// Constructs a per-account statement allowance storage key.
169///
170/// # Arguments
171/// * `account_id` - Account identifier as byte slice
172///
173/// # Returns
174/// Storage key: `":statement_allowance:" ++ account_id`
175pub 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
181/// Increase the statement allowance by the given amount.
182pub 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
189/// Decrease the statement allowance by the given amount.
190pub 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
201/// Get the statement allowance for the given account.
202pub 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
227/// Statement-store specific ed25519 crypto primitives.
228pub mod ed25519 {
229	mod app_ed25519 {
230		use sp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT};
231		app_crypto!(ed25519, STATEMENT);
232	}
233	/// Statement-store specific ed25519 public key.
234	pub type Public = app_ed25519::Public;
235	/// Statement-store specific ed25519 key pair.
236	#[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/// Returns blake2-256 hash for the encoded statement.
249#[cfg(feature = "std")]
250pub fn hash_encoded(data: &[u8]) -> [u8; 32] {
251	sp_crypto_hashing::blake2_256(data)
252}
253
254/// Statement proof.
255#[derive(
256	Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, Debug, Clone, PartialEq, Eq,
257)]
258pub enum Proof {
259	/// Sr25519 Signature.
260	Sr25519 {
261		/// Signature.
262		signature: [u8; 64],
263		/// Public key.
264		signer: [u8; 32],
265	},
266	/// Ed25519 Signature.
267	Ed25519 {
268		/// Signature.
269		signature: [u8; 64],
270		/// Public key.
271		signer: [u8; 32],
272	},
273	/// Secp256k1 Signature.
274	Secp256k1Ecdsa {
275		/// Signature.
276		signature: [u8; 65],
277		/// Public key.
278		signer: [u8; 33],
279	},
280}
281
282impl Proof {
283	/// Return account id for the proof creator.
284	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/// Statement attributes. Each statement is a list of 0 or more fields. Fields may only appear once
296/// and in the order declared here.
297#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
298#[repr(u8)]
299pub enum Field {
300	/// Statement proof.
301	AuthenticityProof(Proof) = 0,
302	/// An identifier for the key that `Data` field may be decrypted with.
303	DecryptionKey(DecryptionKey) = 1,
304	/// Expiry of the statement. See [`Statement::expiry`] for details on the format.
305	Expiry(u64) = 2,
306	/// Account channel to use. Only one message per `(account, channel)` pair is allowed.
307	Channel(Channel) = 3,
308	/// First statement topic.
309	Topic1(Topic) = 4,
310	/// Second statement topic.
311	Topic2(Topic) = 5,
312	/// Third statement topic.
313	Topic3(Topic) = 6,
314	/// Fourth statement topic.
315	Topic4(Topic) = 7,
316	/// Additional data.
317	Data(Vec<u8>) = 8,
318}
319
320impl Field {
321	fn discriminant(&self) -> u8 {
322		// This is safe for repr(u8)
323		// see https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting
324		unsafe { *(self as *const Self as *const u8) }
325	}
326}
327
328/// Statement structure.
329#[derive(DecodeWithMemTracking, Debug, Clone, PartialEq, Eq, Default)]
330pub struct Statement {
331	/// Proof used for authorizing the statement.
332	proof: Option<Proof>,
333	/// An identifier for the key that `Data` field may be decrypted with.
334	#[deprecated(note = "Experimental feature, may be removed/changed in future releases")]
335	decryption_key: Option<DecryptionKey>,
336	/// Used for identifying a distinct communication channel, only a message per channel is
337	/// stored.
338	///
339	/// This can be used to implement message replacement, submitting a new message with a
340	/// different topic/data on the same channel and a greater expiry replaces the previous one.
341	///
342	/// If the new statement data is bigger than the old one, submitting a statement with the same
343	/// channel does not guarantee that **ONLY** the old one will be replaced, as it might not fit
344	/// in the account quota. In that case, other statements from the same account with the lowest
345	/// expiry will be removed.
346	channel: Option<Channel>,
347	/// Message expiry, used for determining which statements to keep.
348	///
349	/// The most significant 32 bits represents the expiration timestamp (in seconds since
350	/// UNIX epoch) after which the statement gets removed. These ensure that statements with a
351	/// higher expiration time have a higher priority.
352	/// The lower 32 bits represents an arbitrary sequence number used to order statements with the
353	/// same expiration time.
354	///
355	/// Higher values indicate a higher priority.
356	/// This is used in two cases:
357	/// 1) When an account exceeds its quota and some statements need to be removed. Statements
358	///    with the lowest `expiry` are removed first.
359	/// 2) When multiple statements are submitted on the same channel, the one with the highest
360	///    expiry replaces the one with the same channel.
361	expiry: u64,
362	/// Number of topics present.
363	num_topics: u8,
364	/// Topics, used for querying and filtering statements.
365	topics: [Topic; MAX_TOPICS],
366	/// Statement data.
367	data: Option<Vec<u8>>,
368}
369
370/// Note: The `TypeInfo` implementation reflects the actual encoding format (`Vec<Field>`)
371/// rather than the struct fields, since `Statement` has custom `Encode`/`Decode` implementations.
372impl TypeInfo for Statement {
373	type Identity = Self;
374
375	fn type_info() -> Type {
376		// Statement encodes as Vec<Field>, so we report the same type info
377		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		// Encoding matches that of Vec<Field>. Basically this just means accepting that there
387		// will be a prefix of vector length.
388		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)]
420/// Result returned by `Statement::verify_signature`
421pub enum SignatureVerificationResult {
422	/// Signature is valid and matches this account id.
423	Valid(AccountId),
424	/// Signature has failed verification.
425	Invalid,
426	/// No signature in the proof or no proof.
427	NoSignature,
428}
429
430impl Statement {
431	/// Create a new empty statement with no proof.
432	pub fn new() -> Statement {
433		Default::default()
434	}
435
436	/// Create a new statement with a proof.
437	pub fn new_with_proof(proof: Proof) -> Statement {
438		let mut statement = Self::new();
439		statement.set_proof(proof);
440		statement
441	}
442
443	/// Sign with a key that matches given public key in the keystore.
444	///
445	/// Returns `true` if signing worked (private key present etc).
446	///
447	/// NOTE: This can only be called from the runtime.
448	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	/// Returns slice of all topics set in the statement.
463	pub fn topics(&self) -> &[Topic] {
464		&self.topics[..self.num_topics as usize]
465	}
466
467	/// Sign with a given private key and add the signature proof field.
468	#[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	/// Sign with a key that matches given public key in the keystore.
477	///
478	/// Returns `true` if signing worked (private key present etc).
479	///
480	/// NOTE: This can only be called from the runtime.
481	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	/// Sign with a given private key and add the signature proof field.
496	#[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	/// Sign with a key that matches given public key in the keystore.
505	///
506	/// Returns `true` if signing worked (private key present etc).
507	///
508	/// NOTE: This can only be called from the runtime.
509	///
510	/// Returns `true` if signing worked (private key present etc).
511	///
512	/// NOTE: This can only be called from the runtime.
513	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	/// Sign with a given private key and add the signature proof field.
528	#[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	/// Check proof signature, if any.
537	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	/// Calculate statement hash.
578	#[cfg(feature = "std")]
579	pub fn hash(&self) -> [u8; 32] {
580		self.using_encoded(hash_encoded)
581	}
582
583	/// Returns a topic by topic index.
584	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	/// Returns decryption key if any.
593	#[allow(deprecated)]
594	pub fn decryption_key(&self) -> Option<DecryptionKey> {
595		self.decryption_key
596	}
597
598	/// Convert to internal data.
599	pub fn into_data(self) -> Option<Vec<u8>> {
600		self.data
601	}
602
603	/// Get a reference to the statement proof, if any.
604	pub fn proof(&self) -> Option<&Proof> {
605		self.proof.as_ref()
606	}
607
608	/// Get proof account id, if any
609	pub fn account_id(&self) -> Option<AccountId> {
610		self.proof.as_ref().map(Proof::account_id)
611	}
612
613	/// Get plain data.
614	pub fn data(&self) -> Option<&Vec<u8>> {
615		self.data.as_ref()
616	}
617
618	/// Get plain data len.
619	pub fn data_len(&self) -> usize {
620		self.data().map_or(0, Vec::len)
621	}
622
623	/// Get channel, if any.
624	pub fn channel(&self) -> Option<Channel> {
625		self.channel
626	}
627
628	/// Get expiry.
629	pub fn expiry(&self) -> u64 {
630		self.expiry
631	}
632
633	/// Get expiration timestamp in seconds.
634	///
635	/// The expiration timestamp in seconds is stored in the most significant 32 bits of the expiry
636	/// field.
637	pub fn get_expiration_timestamp_secs(&self) -> u32 {
638		(self.expiry >> 32) as u32
639	}
640
641	/// Return encoded fields that can be signed to construct or verify a proof
642	fn signature_material(&self) -> Vec<u8> {
643		self.encoded(true)
644	}
645
646	/// Remove the proof of this statement.
647	pub fn remove_proof(&mut self) {
648		self.proof = None;
649	}
650
651	/// Set statement proof. Any existing proof is overwritten.
652	pub fn set_proof(&mut self, proof: Proof) {
653		self.proof = Some(proof)
654	}
655
656	/// Set statement expiry.
657	pub fn set_expiry(&mut self, expiry: u64) {
658		self.expiry = expiry;
659	}
660
661	/// Set statement expiry from its parts. See [`Statement::expiry`] for details on the format.
662	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	/// Set statement channel.
667	pub fn set_channel(&mut self, channel: Channel) {
668		self.channel = Some(channel)
669	}
670
671	/// Set topic by index. Does noting if index is over `MAX_TOPICS`.
672	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	/// Set decryption key.
680	#[allow(deprecated)]
681	pub fn set_decryption_key(&mut self, key: DecryptionKey) {
682		self.decryption_key = Some(key);
683	}
684
685	/// Set unencrypted statement data.
686	pub fn set_plain_data(&mut self, data: Vec<u8>) {
687		self.data = Some(data)
688	}
689
690	/// Estimate the encoded size for preallocation.
691	///
692	/// Returns a close approximation of the SCALE-encoded size without actually performing the
693	/// encoding. Uses max_encoded_len() for type sizes:
694	/// - Compact length prefix: max_encoded_len() bytes
695	/// - Proof field: 1 (tag) + max_encoded_len()
696	/// - DecryptionKey: 1 (tag) + max_encoded_len()
697	/// - Expiry: 1 (tag) + max_encoded_len()
698	/// - Channel: 1 (tag) + max_encoded_len()
699	/// - Each topic: 1 (tag) + max_encoded_len()
700	/// - Data: 1 (tag) + max_encoded_len() (compact len) + data.len()
701	#[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		// Encoding matches that of Vec<Field>. Basically this just means accepting that there
728		// will be a prefix of vector length.
729		// Expiry field is always present.
730		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		// When encoding signature payload, the length prefix is omitted.
738		// This is so that the signature for encoded statement can potentially be derived without
739		// needing to re-encode the statement.
740		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	/// Encrypt give data with given key and store both in the statements.
773	#[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	/// Decrypt data (if any) with the given private key.
787	#[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		// Take a valid encoded statement and corrupt it in different ways
889		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		// Truncate to just the length prefix
894		assert!(Statement::decode(&mut &valid[..1][..]).is_err());
895
896		// Replace field discriminant with invalid value (Field only has 0..=8)
897		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		// Truncate the Expiry payload (need 8 bytes for u64, provide fewer)
905		assert!(Statement::decode(&mut &valid[..5][..]).is_err());
906
907		// Encode a statement with Proof, then corrupt the Proof variant
908		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		// Truncate the Proof payload
920		assert!(Statement::decode(&mut &with_proof[..6][..]).is_err());
921
922		// Claim more fields than actually present
923		let mut inflated_count = valid.clone();
924		inflated_count[0] = 5 << 2; // change field count from 1 to 5
925		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		// set an invalid Sr25519 signature
958		statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
959		assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
960
961		// set an invalid Ed25519 signature
962		statement.set_proof(Proof::Ed25519 { signature: [0xAB; 64], signer: [0xCD; 32] });
963		assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
964
965		// set an invalid Secp256k1Ecdsa signature
966		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		// let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
980		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		// Statement has custom Encode/Decode that encodes as Vec<Field>.
1020		// Verify that TypeInfo reflects this by containing a reference to Vec<Field>.
1021		let statement_type = Statement::type_info();
1022		let vec_field_meta = MetaType::new::<Vec<Field>>();
1023
1024		// The Statement type should be a composite with one unnamed field of type Vec<Field>
1025		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		// Create 2000 statements with varying data
1043		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		// Measure time to hash all statements
1057		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		// Verify hashes are unique
1063		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		// Allow some overhead due to using max_encoded_len() approximations.
1070		const MAX_ACCEPTED_OVERHEAD: usize = 33;
1071
1072		// Use Secp256k1Ecdsa: with sig=65 + signer=33 bytes, it is the worst-case proof payload
1073		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		// Test with all fields populated
1080		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		// Test for_signing = true (no proof, no compact prefix)
1108		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		// Test with minimal statement (empty)
1126		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	// Wire-format regression tests.
1146	//
1147	// `Proof::OnChain` was removed in favour of cryptographic-only proofs.
1148	// These tests pin the SCALE encoding of the surviving variants so that any future reordering,
1149	// renaming, or payload change is caught immediately.
1150
1151	/// Canonical fixture: a `Statement` with three topics, a channel, an expiry,
1152	/// and a 4-byte payload. Used by every wire-format test in this section.
1153	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	/// The "tail" of every canonical fixture: everything after the optional
1163	/// `AuthenticityProof` field. Pulled out so each variant fixture is one
1164	/// short, reviewable block.
1165	fn canonical_tail() -> Vec<u8> {
1166		let mut v = Vec::new();
1167		v.push(0x02); // Field::Expiry discriminant
1168		v.extend_from_slice(&[0x34, 0x12, 0xcd, 0xab, 0xff, 0xff, 0xff, 0x7f]); // u64 LE
1169		v.push(0x03); // Field::Channel discriminant
1170		v.extend_from_slice(&[0xcc; 32]);
1171		v.push(0x04); // Field::Topic1 discriminant
1172		v.extend_from_slice(&[0x01; 32]);
1173		v.push(0x05); // Field::Topic2 discriminant
1174		v.extend_from_slice(&[0x02; 32]);
1175		v.push(0x06); // Field::Topic3 discriminant
1176		v.extend_from_slice(&[0x03; 32]);
1177		v.push(0x08); // Field::Data discriminant
1178		v.push(0x10); // Compact<u32> = 4
1179		v.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
1180		v
1181	}
1182
1183	/// Pinned byte fixture for `Proof::Sr25519` statements.
1184	#[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); // Compact<u32> = 7 fields (7 << 2)
1192		expected.push(0x00); // Field::AuthenticityProof discriminant
1193		expected.push(0x00); // Proof::Sr25519 discriminant
1194		expected.extend_from_slice(&[0x11; 64]); // signature
1195		expected.extend_from_slice(&[0xAA; 32]); // signer
1196		expected.extend(canonical_tail());
1197
1198		assert_eq!(stmt.encode(), expected, "Sr25519 wire format drifted");
1199		assert_eq!(expected.len(), 246);
1200		// Round-trip
1201		assert_eq!(Statement::decode(&mut expected.as_slice()).unwrap(), stmt);
1202	}
1203
1204	/// `Proof::OnChain` byte sequences are rejected on decode.
1205	#[test]
1206	fn wire_format_legacy_onchain_proof_is_rejected() {
1207		// Hand-crafted SCALE: 2 fields = [AuthenticityProof(OnChain { ... }), Expiry].
1208		let mut legacy = Vec::new();
1209		legacy.push(0x08); // Compact<u32> = 2 fields
1210		legacy.push(0x00); // Field::AuthenticityProof discriminant
1211		legacy.push(0x03); // Proof variant discriminant 3 (the old OnChain slot)
1212		legacy.extend_from_slice(&[0xdd; 32]); // who
1213		legacy.extend_from_slice(&[0xee; 32]); // block_hash
1214		legacy.extend_from_slice(&[0xbe, 0xba, 0xfe, 0xca, 0xef, 0xbe, 0xad, 0xde]); // event_index
1215		legacy.push(0x02); // Field::Expiry
1216		legacy.extend_from_slice(&[0x2a, 0, 0, 0, 0, 0, 0, 0]); // 42 as u64 LE
1217
1218		assert!(
1219			Statement::decode(&mut legacy.as_slice()).is_err(),
1220			"legacy OnChain bytes must no longer decode into a Statement",
1221		);
1222
1223		// And the same payload with the discriminant moved into the survivor
1224		// range still works — proving the rejection is specifically about the
1225		// removed slot, not a wholesale break of the codec.
1226		let mut survivor = Vec::new();
1227		survivor.push(0x08);
1228		survivor.push(0x00);
1229		survivor.push(0x00); // Proof::Sr25519 — survivor variant
1230		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	/// `Proof::max_encoded_len()` reflects the three-variant enum.
1241	#[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}