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