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