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