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