Skip to main content

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