1#![cfg_attr(not(feature = "std"), no_std)]
35
36use frame_support::{
37 pallet_prelude::*,
38 sp_runtime::{traits::CheckedDiv, SaturatedConversion},
39 traits::fungible::Inspect,
40};
41use frame_system::pallet_prelude::*;
42use sp_statement_store::{
43 runtime_api::{InvalidStatement, StatementSource, ValidStatement},
44 Proof, SignatureVerificationResult, Statement,
45};
46
47#[cfg(test)]
48#[allow(unexpected_cfgs)]
50mod mock;
51#[cfg(test)]
52mod tests;
53
54pub use pallet::*;
55
56const LOG_TARGET: &str = "runtime::statement";
57
58#[frame_support::pallet]
59pub mod pallet {
60 use super::*;
61
62 pub type BalanceOf<T> =
63 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
64
65 pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
66
67 #[pallet::config]
68 pub trait Config: frame_system::Config
69 where
70 <Self as frame_system::Config>::AccountId: From<sp_statement_store::AccountId>,
71 {
72 #[allow(deprecated)]
74 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
75 type Currency: Inspect<Self::AccountId>;
77 #[pallet::constant]
79 type StatementCost: Get<BalanceOf<Self>>;
80 #[pallet::constant]
82 type ByteCost: Get<BalanceOf<Self>>;
83 #[pallet::constant]
85 type MinAllowedStatements: Get<u32>;
86 #[pallet::constant]
88 type MaxAllowedStatements: Get<u32>;
89 #[pallet::constant]
91 type MinAllowedBytes: Get<u32>;
92 #[pallet::constant]
94 type MaxAllowedBytes: Get<u32>;
95 }
96
97 #[pallet::pallet]
98 pub struct Pallet<T>(core::marker::PhantomData<T>);
99
100 #[pallet::event]
101 #[pallet::generate_deposit(pub(super) fn deposit_event)]
102 pub enum Event<T: Config>
103 where
104 <T as frame_system::Config>::AccountId: From<sp_statement_store::AccountId>,
105 {
106 NewStatement { account: T::AccountId, statement: Statement },
108 }
109
110 #[pallet::hooks]
111 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
112 where
113 <T as frame_system::Config>::AccountId: From<sp_statement_store::AccountId>,
114 sp_statement_store::AccountId: From<<T as frame_system::Config>::AccountId>,
115 <T as frame_system::Config>::RuntimeEvent: From<pallet::Event<T>>,
116 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet::Event<T>>,
117 sp_statement_store::BlockHash: From<<T as frame_system::Config>::Hash>,
118 {
119 fn offchain_worker(now: BlockNumberFor<T>) {
120 log::trace!(target: LOG_TARGET, "Collecting statements at #{:?}", now);
121 Pallet::<T>::collect_statements();
122 }
123 }
124}
125
126impl<T: Config> Pallet<T>
127where
128 <T as frame_system::Config>::AccountId: From<sp_statement_store::AccountId>,
129 sp_statement_store::AccountId: From<<T as frame_system::Config>::AccountId>,
130 <T as frame_system::Config>::RuntimeEvent: From<pallet::Event<T>>,
131 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet::Event<T>>,
132 sp_statement_store::BlockHash: From<<T as frame_system::Config>::Hash>,
133{
134 pub fn validate_statement(
137 _source: StatementSource,
138 mut statement: Statement,
139 ) -> Result<ValidStatement, InvalidStatement> {
140 sp_io::init_tracing();
141 log::debug!(target: LOG_TARGET, "Validating statement {:?}", statement);
142 let account: T::AccountId = match statement.proof() {
143 Some(Proof::OnChain { who, block_hash, event_index }) => {
144 if frame_system::Pallet::<T>::parent_hash().as_ref() != block_hash.as_slice() {
145 log::debug!(target: LOG_TARGET, "Bad block hash.");
146 return Err(InvalidStatement::BadProof)
147 }
148 let account: T::AccountId = (*who).into();
149 match frame_system::Pallet::<T>::event_no_consensus(*event_index as usize) {
150 Some(e) => {
151 statement.remove_proof();
152 if let Ok(Event::NewStatement { account: a, statement: s }) = e.try_into() {
153 if a != account || s != statement {
154 log::debug!(target: LOG_TARGET, "Event data mismatch");
155 return Err(InvalidStatement::BadProof)
156 }
157 } else {
158 log::debug!(target: LOG_TARGET, "Event type mismatch");
159 return Err(InvalidStatement::BadProof)
160 }
161 },
162 _ => {
163 log::debug!(target: LOG_TARGET, "Bad event index");
164 return Err(InvalidStatement::BadProof)
165 },
166 }
167 account
168 },
169 _ => match statement.verify_signature() {
170 SignatureVerificationResult::Valid(account) => account.into(),
171 SignatureVerificationResult::Invalid => {
172 log::debug!(target: LOG_TARGET, "Bad statement signature.");
173 return Err(InvalidStatement::BadProof)
174 },
175 SignatureVerificationResult::NoSignature => {
176 log::debug!(target: LOG_TARGET, "Missing statement signature.");
177 return Err(InvalidStatement::NoProof)
178 },
179 },
180 };
181 let statement_cost = T::StatementCost::get();
182 let byte_cost = T::ByteCost::get();
183 let balance = <T::Currency as Inspect<AccountIdOf<T>>>::balance(&account);
184 let min_allowed_statements = T::MinAllowedStatements::get();
185 let max_allowed_statements = T::MaxAllowedStatements::get();
186 let min_allowed_bytes = T::MinAllowedBytes::get();
187 let max_allowed_bytes = T::MaxAllowedBytes::get();
188 let max_count = balance
189 .checked_div(&statement_cost)
190 .unwrap_or_default()
191 .saturated_into::<u32>()
192 .clamp(min_allowed_statements, max_allowed_statements);
193 let max_size = balance
194 .checked_div(&byte_cost)
195 .unwrap_or_default()
196 .saturated_into::<u32>()
197 .clamp(min_allowed_bytes, max_allowed_bytes);
198
199 Ok(ValidStatement { max_count, max_size })
200 }
201
202 pub fn submit_statement(account: T::AccountId, statement: Statement) {
205 Self::deposit_event(Event::NewStatement { account, statement });
206 }
207
208 fn collect_statements() {
209 for (index, event) in frame_system::Pallet::<T>::read_events_no_consensus().enumerate() {
211 if let Ok(Event::<T>::NewStatement { account, mut statement }) = event.event.try_into()
212 {
213 if statement.proof().is_none() {
214 let proof = Proof::OnChain {
215 who: account.into(),
216 block_hash: frame_system::Pallet::<T>::parent_hash().into(),
217 event_index: index as u64,
218 };
219 statement.set_proof(proof);
220 }
221 sp_statement_store::runtime_api::statement_store::submit_statement(statement);
222 }
223 }
224 }
225}