referrerpolicy=no-referrer-when-downgrade

sp_statement_store/
runtime_api.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//! Runtime support for the statement store.
19
20use crate::{Hash, Statement, Topic};
21use alloc::vec::Vec;
22use codec::{Decode, Encode};
23use scale_info::TypeInfo;
24use sp_runtime::RuntimeDebug;
25use sp_runtime_interface::{
26	pass_by::{
27		AllocateAndReturnByCodec, PassFatPointerAndDecode, PassFatPointerAndDecodeSlice,
28		PassPointerAndRead, PassPointerAndReadCopy, ReturnAs,
29	},
30	runtime_interface,
31};
32
33#[cfg(feature = "std")]
34use sp_externalities::ExternalitiesExt;
35
36/// Information concerning a valid statement.
37#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
38pub struct ValidStatement {
39	/// Max statement count for this account, as calculated by the runtime.
40	pub max_count: u32,
41	/// Max total data size for this account, as calculated by the runtime.
42	pub max_size: u32,
43}
44
45/// An reason for an invalid statement.
46#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)]
47pub enum InvalidStatement {
48	/// Failed proof validation.
49	BadProof,
50	/// Missing proof.
51	NoProof,
52	/// Validity could not be checked because of internal error.
53	InternalError,
54}
55
56/// The source of the statement.
57///
58/// Depending on the source we might apply different validation schemes.
59#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
60pub enum StatementSource {
61	/// Statement is coming from the on-chain worker.
62	Chain,
63	/// Statement has been received from the gossip network.
64	Network,
65	/// Statement has been submitted over the local api.
66	Local,
67}
68
69impl StatementSource {
70	/// Check if the source allows the statement to be resubmitted to the store, extending its
71	/// expiration date.
72	pub fn can_be_resubmitted(&self) -> bool {
73		match self {
74			StatementSource::Chain | StatementSource::Local => true,
75			StatementSource::Network => false,
76		}
77	}
78}
79
80sp_api::decl_runtime_apis! {
81	/// Runtime API trait for statement validation.
82	pub trait ValidateStatement {
83		/// Validate the statement.
84		fn validate_statement(
85			source: StatementSource,
86			statement: Statement,
87		) -> Result<ValidStatement, InvalidStatement>;
88	}
89}
90
91#[cfg(feature = "std")]
92sp_externalities::decl_extension! {
93	/// The offchain database extension that will be registered at the Substrate externalities.
94	pub struct StatementStoreExt(std::sync::Arc<dyn crate::StatementStore>);
95}
96
97// Host extensions for the runtime.
98#[cfg(feature = "std")]
99impl StatementStoreExt {
100	/// Create new instance of externalities extensions.
101	pub fn new(store: std::sync::Arc<dyn crate::StatementStore>) -> Self {
102		Self(store)
103	}
104}
105
106/// Submission result.
107#[derive(Debug, Eq, PartialEq, Clone, Copy, Encode, Decode)]
108pub enum SubmitResult {
109	/// Accepted as new.
110	OkNew = 0,
111	/// Known statement
112	OkKnown = 1,
113	/// Statement failed validation.
114	Bad = 2,
115	/// The store is not available.
116	NotAvailable = 3,
117	/// Statement could not be inserted because of priority or size checks.
118	Full = 4,
119}
120
121impl TryFrom<u8> for SubmitResult {
122	type Error = ();
123	fn try_from(value: u8) -> Result<Self, Self::Error> {
124		match value {
125			0 => Ok(SubmitResult::OkNew),
126			1 => Ok(SubmitResult::OkKnown),
127			2 => Ok(SubmitResult::Bad),
128			3 => Ok(SubmitResult::NotAvailable),
129			4 => Ok(SubmitResult::Full),
130			_ => Err(()),
131		}
132	}
133}
134
135impl From<SubmitResult> for u8 {
136	fn from(value: SubmitResult) -> Self {
137		value as u8
138	}
139}
140
141/// Export functions for the WASM host.
142#[cfg(feature = "std")]
143pub type HostFunctions = (statement_store::HostFunctions,);
144
145/// Host interface
146#[runtime_interface]
147pub trait StatementStore {
148	/// Submit a new new statement. The statement will be broadcast to the network.
149	/// This is meant to be used by the offchain worker.
150	fn submit_statement(
151		&mut self,
152		statement: PassFatPointerAndDecode<Statement>,
153	) -> ReturnAs<SubmitResult, u8> {
154		if let Some(StatementStoreExt(store)) = self.extension::<StatementStoreExt>() {
155			match store.submit(statement, StatementSource::Chain) {
156				crate::SubmitResult::New(_) => SubmitResult::OkNew,
157				crate::SubmitResult::Known => SubmitResult::OkKnown,
158				crate::SubmitResult::Ignored => SubmitResult::Full,
159				// This should not happen for `StatementSource::Chain`. An existing statement will
160				// be overwritten.
161				crate::SubmitResult::KnownExpired => SubmitResult::Bad,
162				crate::SubmitResult::Bad(_) => SubmitResult::Bad,
163				crate::SubmitResult::InternalError(_) => SubmitResult::Bad,
164			}
165		} else {
166			SubmitResult::NotAvailable
167		}
168	}
169
170	/// Return all statements.
171	fn statements(&mut self) -> AllocateAndReturnByCodec<Vec<(Hash, Statement)>> {
172		if let Some(StatementStoreExt(store)) = self.extension::<StatementStoreExt>() {
173			store.statements().unwrap_or_default()
174		} else {
175			Vec::default()
176		}
177	}
178
179	/// Return the data of all known statements which include all topics and have no `DecryptionKey`
180	/// field.
181	fn broadcasts(
182		&mut self,
183		match_all_topics: PassFatPointerAndDecodeSlice<&[Topic]>,
184	) -> AllocateAndReturnByCodec<Vec<Vec<u8>>> {
185		if let Some(StatementStoreExt(store)) = self.extension::<StatementStoreExt>() {
186			store.broadcasts(match_all_topics).unwrap_or_default()
187		} else {
188			Vec::default()
189		}
190	}
191
192	/// Return the data of all known statements whose decryption key is identified as `dest` (this
193	/// will generally be the public key or a hash thereof for symmetric ciphers, or a hash of the
194	/// private key for symmetric ciphers).
195	fn posted(
196		&mut self,
197		match_all_topics: PassFatPointerAndDecodeSlice<&[Topic]>,
198		dest: PassPointerAndReadCopy<[u8; 32], 32>,
199	) -> AllocateAndReturnByCodec<Vec<Vec<u8>>> {
200		if let Some(StatementStoreExt(store)) = self.extension::<StatementStoreExt>() {
201			store.posted(match_all_topics, dest).unwrap_or_default()
202		} else {
203			Vec::default()
204		}
205	}
206
207	/// Return the decrypted data of all known statements whose decryption key is identified as
208	/// `dest`. The key must be available to the client.
209	fn posted_clear(
210		&mut self,
211		match_all_topics: PassFatPointerAndDecodeSlice<&[Topic]>,
212		dest: PassPointerAndReadCopy<[u8; 32], 32>,
213	) -> AllocateAndReturnByCodec<Vec<Vec<u8>>> {
214		if let Some(StatementStoreExt(store)) = self.extension::<StatementStoreExt>() {
215			store.posted_clear(match_all_topics, dest).unwrap_or_default()
216		} else {
217			Vec::default()
218		}
219	}
220
221	/// Remove a statement from the store by hash.
222	fn remove(&mut self, hash: PassPointerAndRead<&Hash, 32>) {
223		if let Some(StatementStoreExt(store)) = self.extension::<StatementStoreExt>() {
224			store.remove(hash).unwrap_or_default()
225		}
226	}
227}