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::{Decode, DecodeWithMemTracking, Encode};
27use scale_info::TypeInfo;
28use sp_application_crypto::RuntimeAppPublic;
29#[cfg(feature = "std")]
30use sp_core::Pair;
31
32/// Statement topic.
33pub type Topic = [u8; 32];
34/// Decryption key identifier.
35pub type DecryptionKey = [u8; 32];
36/// Statement hash.
37pub type Hash = [u8; 32];
38/// Block hash.
39pub type BlockHash = [u8; 32];
40/// Account id
41pub type AccountId = [u8; 32];
42/// Statement channel.
43pub type Channel = [u8; 32];
44
45/// Total number of topic fields allowed.
46pub const MAX_TOPICS: usize = 4;
47
48#[cfg(feature = "std")]
49pub use store_api::{
50	Error, InvalidReason, RejectionReason, Result, StatementSource, StatementStore, SubmitResult,
51};
52
53#[cfg(feature = "std")]
54mod ecies;
55pub mod runtime_api;
56#[cfg(feature = "std")]
57mod store_api;
58
59mod sr25519 {
60	mod app_sr25519 {
61		use sp_application_crypto::{app_crypto, key_types::STATEMENT, sr25519};
62		app_crypto!(sr25519, STATEMENT);
63	}
64	pub type Public = app_sr25519::Public;
65}
66
67/// Statement-store specific ed25519 crypto primitives.
68pub mod ed25519 {
69	mod app_ed25519 {
70		use sp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT};
71		app_crypto!(ed25519, STATEMENT);
72	}
73	/// Statement-store specific ed25519 public key.
74	pub type Public = app_ed25519::Public;
75	/// Statement-store specific ed25519 key pair.
76	#[cfg(feature = "std")]
77	pub type Pair = app_ed25519::Pair;
78}
79
80mod ecdsa {
81	mod app_ecdsa {
82		use sp_application_crypto::{app_crypto, ecdsa, key_types::STATEMENT};
83		app_crypto!(ecdsa, STATEMENT);
84	}
85	pub type Public = app_ecdsa::Public;
86}
87
88/// Returns blake2-256 hash for the encoded statement.
89#[cfg(feature = "std")]
90pub fn hash_encoded(data: &[u8]) -> [u8; 32] {
91	sp_crypto_hashing::blake2_256(data)
92}
93
94/// Statement proof.
95#[derive(Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug, Clone, PartialEq, Eq)]
96pub enum Proof {
97	/// Sr25519 Signature.
98	Sr25519 {
99		/// Signature.
100		signature: [u8; 64],
101		/// Public key.
102		signer: [u8; 32],
103	},
104	/// Ed25519 Signature.
105	Ed25519 {
106		/// Signature.
107		signature: [u8; 64],
108		/// Public key.
109		signer: [u8; 32],
110	},
111	/// Secp256k1 Signature.
112	Secp256k1Ecdsa {
113		/// Signature.
114		signature: [u8; 65],
115		/// Public key.
116		signer: [u8; 33],
117	},
118	/// On-chain event proof.
119	OnChain {
120		/// Account identifier associated with the event.
121		who: AccountId,
122		/// Hash of block that contains the event.
123		block_hash: BlockHash,
124		/// Index of the event in the event list.
125		event_index: u64,
126	},
127}
128
129impl Proof {
130	/// Return account id for the proof creator.
131	pub fn account_id(&self) -> AccountId {
132		match self {
133			Proof::Sr25519 { signer, .. } => *signer,
134			Proof::Ed25519 { signer, .. } => *signer,
135			Proof::Secp256k1Ecdsa { signer, .. } =>
136				<sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer).into(),
137			Proof::OnChain { who, .. } => *who,
138		}
139	}
140}
141
142/// Statement attributes. Each statement is a list of 0 or more fields. Fields may only appear once
143/// and in the order declared here.
144#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
145#[repr(u8)]
146pub enum Field {
147	/// Statement proof.
148	AuthenticityProof(Proof) = 0,
149	/// An identifier for the key that `Data` field may be decrypted with.
150	DecryptionKey(DecryptionKey) = 1,
151	/// Priority when competing with other messages from the same sender.
152	Priority(u32) = 2,
153	/// Account channel to use. Only one message per `(account, channel)` pair is allowed.
154	Channel(Channel) = 3,
155	/// First statement topic.
156	Topic1(Topic) = 4,
157	/// Second statement topic.
158	Topic2(Topic) = 5,
159	/// Third statement topic.
160	Topic3(Topic) = 6,
161	/// Fourth statement topic.
162	Topic4(Topic) = 7,
163	/// Additional data.
164	Data(Vec<u8>) = 8,
165}
166
167impl Field {
168	fn discriminant(&self) -> u8 {
169		// This is safe for repr(u8)
170		// see https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting
171		unsafe { *(self as *const Self as *const u8) }
172	}
173}
174
175/// Statement structure.
176#[derive(DecodeWithMemTracking, TypeInfo, Debug, Clone, PartialEq, Eq, Default)]
177pub struct Statement {
178	proof: Option<Proof>,
179	decryption_key: Option<DecryptionKey>,
180	channel: Option<Channel>,
181	priority: Option<u32>,
182	num_topics: u8,
183	topics: [Topic; MAX_TOPICS],
184	data: Option<Vec<u8>>,
185}
186
187impl Decode for Statement {
188	fn decode<I: codec::Input>(input: &mut I) -> core::result::Result<Self, codec::Error> {
189		// Encoding matches that of Vec<Field>. Basically this just means accepting that there
190		// will be a prefix of vector length.
191		let num_fields: codec::Compact<u32> = Decode::decode(input)?;
192		let mut tag = 0;
193		let mut statement = Statement::new();
194		for i in 0..num_fields.into() {
195			let field: Field = Decode::decode(input)?;
196			if i > 0 && field.discriminant() <= tag {
197				return Err("Invalid field order or duplicate fields".into())
198			}
199			tag = field.discriminant();
200			match field {
201				Field::AuthenticityProof(p) => statement.set_proof(p),
202				Field::DecryptionKey(key) => statement.set_decryption_key(key),
203				Field::Priority(p) => statement.set_priority(p),
204				Field::Channel(c) => statement.set_channel(c),
205				Field::Topic1(t) => statement.set_topic(0, t),
206				Field::Topic2(t) => statement.set_topic(1, t),
207				Field::Topic3(t) => statement.set_topic(2, t),
208				Field::Topic4(t) => statement.set_topic(3, t),
209				Field::Data(data) => statement.set_plain_data(data),
210			}
211		}
212		Ok(statement)
213	}
214}
215
216impl Encode for Statement {
217	fn encode(&self) -> Vec<u8> {
218		self.encoded(false)
219	}
220}
221
222#[derive(Clone, Copy, PartialEq, Eq, Debug)]
223/// Result returned by `Statement::verify_signature`
224pub enum SignatureVerificationResult {
225	/// Signature is valid and matches this account id.
226	Valid(AccountId),
227	/// Signature has failed verification.
228	Invalid,
229	/// No signature in the proof or no proof.
230	NoSignature,
231}
232
233impl Statement {
234	/// Create a new empty statement with no proof.
235	pub fn new() -> Statement {
236		Default::default()
237	}
238
239	/// Create a new statement with a proof.
240	pub fn new_with_proof(proof: Proof) -> Statement {
241		let mut statement = Self::new();
242		statement.set_proof(proof);
243		statement
244	}
245
246	/// Sign with a key that matches given public key in the keystore.
247	///
248	/// Returns `true` if signing worked (private key present etc).
249	///
250	/// NOTE: This can only be called from the runtime.
251	pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool {
252		let to_sign = self.signature_material();
253		if let Some(signature) = key.sign(&to_sign) {
254			let proof = Proof::Sr25519 {
255				signature: signature.into_inner().into(),
256				signer: key.clone().into_inner().into(),
257			};
258			self.set_proof(proof);
259			true
260		} else {
261			false
262		}
263	}
264
265	/// Sign with a given private key and add the signature proof field.
266	#[cfg(feature = "std")]
267	pub fn sign_sr25519_private(&mut self, key: &sp_core::sr25519::Pair) {
268		let to_sign = self.signature_material();
269		let proof =
270			Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
271		self.set_proof(proof);
272	}
273
274	/// Sign with a key that matches given public key in the keystore.
275	///
276	/// Returns `true` if signing worked (private key present etc).
277	///
278	/// NOTE: This can only be called from the runtime.
279	pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool {
280		let to_sign = self.signature_material();
281		if let Some(signature) = key.sign(&to_sign) {
282			let proof = Proof::Ed25519 {
283				signature: signature.into_inner().into(),
284				signer: key.clone().into_inner().into(),
285			};
286			self.set_proof(proof);
287			true
288		} else {
289			false
290		}
291	}
292
293	/// Sign with a given private key and add the signature proof field.
294	#[cfg(feature = "std")]
295	pub fn sign_ed25519_private(&mut self, key: &sp_core::ed25519::Pair) {
296		let to_sign = self.signature_material();
297		let proof =
298			Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
299		self.set_proof(proof);
300	}
301
302	/// Sign with a key that matches given public key in the keystore.
303	///
304	/// Returns `true` if signing worked (private key present etc).
305	///
306	/// NOTE: This can only be called from the runtime.
307	///
308	/// Returns `true` if signing worked (private key present etc).
309	///
310	/// NOTE: This can only be called from the runtime.
311	pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool {
312		let to_sign = self.signature_material();
313		if let Some(signature) = key.sign(&to_sign) {
314			let proof = Proof::Secp256k1Ecdsa {
315				signature: signature.into_inner().into(),
316				signer: key.clone().into_inner().0,
317			};
318			self.set_proof(proof);
319			true
320		} else {
321			false
322		}
323	}
324
325	/// Sign with a given private key and add the signature proof field.
326	#[cfg(feature = "std")]
327	pub fn sign_ecdsa_private(&mut self, key: &sp_core::ecdsa::Pair) {
328		let to_sign = self.signature_material();
329		let proof =
330			Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 };
331		self.set_proof(proof);
332	}
333
334	/// Check proof signature, if any.
335	pub fn verify_signature(&self) -> SignatureVerificationResult {
336		use sp_runtime::traits::Verify;
337
338		match self.proof() {
339			Some(Proof::OnChain { .. }) | None => SignatureVerificationResult::NoSignature,
340			Some(Proof::Sr25519 { signature, signer }) => {
341				let to_sign = self.signature_material();
342				let signature = sp_core::sr25519::Signature::from(*signature);
343				let public = sp_core::sr25519::Public::from(*signer);
344				if signature.verify(to_sign.as_slice(), &public) {
345					SignatureVerificationResult::Valid(*signer)
346				} else {
347					SignatureVerificationResult::Invalid
348				}
349			},
350			Some(Proof::Ed25519 { signature, signer }) => {
351				let to_sign = self.signature_material();
352				let signature = sp_core::ed25519::Signature::from(*signature);
353				let public = sp_core::ed25519::Public::from(*signer);
354				if signature.verify(to_sign.as_slice(), &public) {
355					SignatureVerificationResult::Valid(*signer)
356				} else {
357					SignatureVerificationResult::Invalid
358				}
359			},
360			Some(Proof::Secp256k1Ecdsa { signature, signer }) => {
361				let to_sign = self.signature_material();
362				let signature = sp_core::ecdsa::Signature::from(*signature);
363				let public = sp_core::ecdsa::Public::from(*signer);
364				if signature.verify(to_sign.as_slice(), &public) {
365					let sender_hash =
366						<sp_runtime::traits::BlakeTwo256 as sp_core::Hasher>::hash(signer);
367					SignatureVerificationResult::Valid(sender_hash.into())
368				} else {
369					SignatureVerificationResult::Invalid
370				}
371			},
372		}
373	}
374
375	/// Calculate statement hash.
376	#[cfg(feature = "std")]
377	pub fn hash(&self) -> [u8; 32] {
378		self.using_encoded(hash_encoded)
379	}
380
381	/// Returns a topic by topic index.
382	pub fn topic(&self, index: usize) -> Option<Topic> {
383		if index < self.num_topics as usize {
384			Some(self.topics[index])
385		} else {
386			None
387		}
388	}
389
390	/// Returns decryption key if any.
391	pub fn decryption_key(&self) -> Option<DecryptionKey> {
392		self.decryption_key
393	}
394
395	/// Convert to internal data.
396	pub fn into_data(self) -> Option<Vec<u8>> {
397		self.data
398	}
399
400	/// Get a reference to the statement proof, if any.
401	pub fn proof(&self) -> Option<&Proof> {
402		self.proof.as_ref()
403	}
404
405	/// Get proof account id, if any
406	pub fn account_id(&self) -> Option<AccountId> {
407		self.proof.as_ref().map(Proof::account_id)
408	}
409
410	/// Get plain data.
411	pub fn data(&self) -> Option<&Vec<u8>> {
412		self.data.as_ref()
413	}
414
415	/// Get plain data len.
416	pub fn data_len(&self) -> usize {
417		self.data().map_or(0, Vec::len)
418	}
419
420	/// Get channel, if any.
421	pub fn channel(&self) -> Option<Channel> {
422		self.channel
423	}
424
425	/// Get priority, if any.
426	pub fn priority(&self) -> Option<u32> {
427		self.priority
428	}
429
430	/// Return encoded fields that can be signed to construct or verify a proof
431	fn signature_material(&self) -> Vec<u8> {
432		self.encoded(true)
433	}
434
435	/// Remove the proof of this statement.
436	pub fn remove_proof(&mut self) {
437		self.proof = None;
438	}
439
440	/// Set statement proof. Any existing proof is overwritten.
441	pub fn set_proof(&mut self, proof: Proof) {
442		self.proof = Some(proof)
443	}
444
445	/// Set statement priority.
446	pub fn set_priority(&mut self, priority: u32) {
447		self.priority = Some(priority)
448	}
449
450	/// Set statement channel.
451	pub fn set_channel(&mut self, channel: Channel) {
452		self.channel = Some(channel)
453	}
454
455	/// Set topic by index. Does noting if index is over `MAX_TOPICS`.
456	pub fn set_topic(&mut self, index: usize, topic: Topic) {
457		if index < MAX_TOPICS {
458			self.topics[index] = topic;
459			self.num_topics = self.num_topics.max(index as u8 + 1);
460		}
461	}
462
463	/// Set decryption key.
464	pub fn set_decryption_key(&mut self, key: DecryptionKey) {
465		self.decryption_key = Some(key);
466	}
467
468	/// Set unencrypted statement data.
469	pub fn set_plain_data(&mut self, data: Vec<u8>) {
470		self.data = Some(data)
471	}
472
473	fn encoded(&self, for_signing: bool) -> Vec<u8> {
474		// Encoding matches that of Vec<Field>. Basically this just means accepting that there
475		// will be a prefix of vector length.
476		let num_fields = if !for_signing && self.proof.is_some() { 1 } else { 0 } +
477			if self.decryption_key.is_some() { 1 } else { 0 } +
478			if self.priority.is_some() { 1 } else { 0 } +
479			if self.channel.is_some() { 1 } else { 0 } +
480			if self.data.is_some() { 1 } else { 0 } +
481			self.num_topics as u32;
482
483		let mut output = Vec::new();
484		// When encoding signature payload, the length prefix is omitted.
485		// This is so that the signature for encoded statement can potentially be derived without
486		// needing to re-encode the statement.
487		if !for_signing {
488			let compact_len = codec::Compact::<u32>(num_fields);
489			compact_len.encode_to(&mut output);
490
491			if let Some(proof) = &self.proof {
492				0u8.encode_to(&mut output);
493				proof.encode_to(&mut output);
494			}
495		}
496		if let Some(decryption_key) = &self.decryption_key {
497			1u8.encode_to(&mut output);
498			decryption_key.encode_to(&mut output);
499		}
500		if let Some(priority) = &self.priority {
501			2u8.encode_to(&mut output);
502			priority.encode_to(&mut output);
503		}
504		if let Some(channel) = &self.channel {
505			3u8.encode_to(&mut output);
506			channel.encode_to(&mut output);
507		}
508		for t in 0..self.num_topics {
509			(4u8 + t).encode_to(&mut output);
510			self.topics[t as usize].encode_to(&mut output);
511		}
512		if let Some(data) = &self.data {
513			8u8.encode_to(&mut output);
514			data.encode_to(&mut output);
515		}
516		output
517	}
518
519	/// Encrypt give data with given key and store both in the statements.
520	#[cfg(feature = "std")]
521	pub fn encrypt(
522		&mut self,
523		data: &[u8],
524		key: &sp_core::ed25519::Public,
525	) -> core::result::Result<(), ecies::Error> {
526		let encrypted = ecies::encrypt_ed25519(key, data)?;
527		self.data = Some(encrypted);
528		self.decryption_key = Some((*key).into());
529		Ok(())
530	}
531
532	/// Decrypt data (if any) with the given private key.
533	#[cfg(feature = "std")]
534	pub fn decrypt_private(
535		&self,
536		key: &sp_core::ed25519::Pair,
537	) -> core::result::Result<Option<Vec<u8>>, ecies::Error> {
538		self.data.as_ref().map(|d| ecies::decrypt_ed25519(key, d)).transpose()
539	}
540}
541
542#[cfg(test)]
543mod test {
544	use crate::{hash_encoded, Field, Proof, SignatureVerificationResult, Statement};
545	use codec::{Decode, Encode};
546	use sp_application_crypto::Pair;
547
548	#[test]
549	fn statement_encoding_matches_vec() {
550		let mut statement = Statement::new();
551		assert!(statement.proof().is_none());
552		let proof = Proof::OnChain { who: [42u8; 32], block_hash: [24u8; 32], event_index: 66 };
553
554		let decryption_key = [0xde; 32];
555		let topic1 = [0x01; 32];
556		let topic2 = [0x02; 32];
557		let data = vec![55, 99];
558		let priority = 999;
559		let channel = [0xcc; 32];
560
561		statement.set_proof(proof.clone());
562		statement.set_decryption_key(decryption_key);
563		statement.set_priority(priority);
564		statement.set_channel(channel);
565		statement.set_topic(0, topic1);
566		statement.set_topic(1, topic2);
567		statement.set_plain_data(data.clone());
568
569		statement.set_topic(5, [0x55; 32]);
570		assert_eq!(statement.topic(5), None);
571
572		let fields = vec![
573			Field::AuthenticityProof(proof.clone()),
574			Field::DecryptionKey(decryption_key),
575			Field::Priority(priority),
576			Field::Channel(channel),
577			Field::Topic1(topic1),
578			Field::Topic2(topic2),
579			Field::Data(data.clone()),
580		];
581
582		let encoded = statement.encode();
583		assert_eq!(statement.hash(), hash_encoded(&encoded));
584		assert_eq!(encoded, fields.encode());
585
586		let decoded = Statement::decode(&mut encoded.as_slice()).unwrap();
587		assert_eq!(decoded, statement);
588	}
589
590	#[test]
591	fn decode_checks_fields() {
592		let topic1 = [0x01; 32];
593		let topic2 = [0x02; 32];
594		let priority = 999;
595
596		let fields = vec![
597			Field::Priority(priority),
598			Field::Topic1(topic1),
599			Field::Topic1(topic1),
600			Field::Topic2(topic2),
601		]
602		.encode();
603
604		assert!(Statement::decode(&mut fields.as_slice()).is_err());
605
606		let fields =
607			vec![Field::Topic1(topic1), Field::Priority(priority), Field::Topic2(topic2)].encode();
608
609		assert!(Statement::decode(&mut fields.as_slice()).is_err());
610	}
611
612	#[test]
613	fn sign_and_verify() {
614		let mut statement = Statement::new();
615		statement.set_plain_data(vec![42]);
616
617		let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
618		let ed25519_kp = sp_core::ed25519::Pair::from_string("//Alice", None).unwrap();
619		let secp256k1_kp = sp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
620
621		statement.sign_sr25519_private(&sr25519_kp);
622		assert_eq!(
623			statement.verify_signature(),
624			SignatureVerificationResult::Valid(sr25519_kp.public().0)
625		);
626
627		statement.sign_ed25519_private(&ed25519_kp);
628		assert_eq!(
629			statement.verify_signature(),
630			SignatureVerificationResult::Valid(ed25519_kp.public().0)
631		);
632
633		statement.sign_ecdsa_private(&secp256k1_kp);
634		assert_eq!(
635			statement.verify_signature(),
636			SignatureVerificationResult::Valid(sp_crypto_hashing::blake2_256(
637				&secp256k1_kp.public().0
638			))
639		);
640
641		// set an invalid signature
642		statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
643		assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
644
645		statement.remove_proof();
646		assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature);
647	}
648
649	#[test]
650	fn encrypt_decrypt() {
651		let mut statement = Statement::new();
652		let (pair, _) = sp_core::ed25519::Pair::generate();
653		let plain = b"test data".to_vec();
654
655		//let sr25519_kp = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
656		statement.encrypt(&plain, &pair.public()).unwrap();
657		assert_ne!(plain.as_slice(), statement.data().unwrap().as_slice());
658
659		let decrypted = statement.decrypt_private(&pair).unwrap();
660		assert_eq!(decrypted, Some(plain));
661	}
662}