Skip to content

Commit 6a5607f

Browse files
committed
pallet: create SignedExtension for validating blob sizes
1 parent 0a2e0c8 commit 6a5607f

File tree

3 files changed

+117
-4
lines changed

3 files changed

+117
-4
lines changed

sugondat-chain/pallets/blobs/src/lib.rs

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ mod tests;
1414
mod benchmarking;
1515
pub mod weights;
1616

17+
use codec::{Decode, Encode};
18+
use scale_info::TypeInfo;
19+
use sp_runtime::{
20+
traits::{DispatchInfoOf, SignedExtension},
21+
transaction_validity::{
22+
InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
23+
},
24+
};
25+
26+
use frame_support::traits::{Get, IsSubType};
27+
1728
#[frame_support::pallet]
1829
pub mod pallet {
1930
pub use crate::weights::WeightInfo;
@@ -153,7 +164,10 @@ pub mod pallet {
153164
//
154165
// To the submit_blob weight is added a flat cost relative to the on_finalize execution
155166
// the amount is equal to the entire weight of on_finalized divided by 1/4 of the MaxBlobs
156-
// this covers perfectly the on_finalize cost if on avarage 1/4 of the possible blobs are submitted in one block
167+
// this covers perfectly the on_finalize cost if on average 1/4 of the possible blobs are submitted in one block
168+
//
169+
// Note: this PANICS if the size of the blob, the total size of all blobs, or the total number of blobs submitted
170+
// exceed their respective configured limits. These panics are intended to be protected against by the [`crate::PrevalidateBlobs`] extension.
157171
#[pallet::weight(
158172
T::WeightInfo::submit_blob(T::MaxBlobs::get() / 2, blob.len() as u32)
159173
.saturating_add(T::WeightInfo::on_finalize(0) / (T::MaxBlobs::get() / 4) as u64)
@@ -165,20 +179,25 @@ pub mod pallet {
165179
) -> DispatchResultWithPostInfo {
166180
let who = ensure_signed(origin)?;
167181

182+
let blob_len = blob.len() as u32;
183+
if blob_len > T::MaxBlobSize::get() {
184+
panic!("Blob size limit exceeded");
185+
}
186+
168187
let Some(extrinsic_index) = <frame_system::Pallet<T>>::extrinsic_index() else {
169188
return Err(Error::<T>::NoExtrinsicIndex.into());
170189
};
171190

172191
let total_blobs = TotalBlobs::<T>::get();
173192
if total_blobs + 1 > T::MaxBlobs::get() {
174-
return Err(Error::<T>::MaxBlobsReached.into());
193+
panic!("Maximum blob limit exceeded");
175194
}
176195
TotalBlobs::<T>::put(total_blobs + 1);
177196

178197
let blob_len = blob.len() as u32;
179198
let total_blobs_size = TotalBlobsSize::<T>::get();
180199
if total_blobs_size + blob_len > T::MaxTotalBlobSize::get() {
181-
return Err(Error::<T>::MaxTotalBlobsSizeReached.into());
200+
panic!("Maximum total blob size exceeded");
182201
}
183202
TotalBlobsSize::<T>::put(total_blobs_size + blob_len);
184203

@@ -208,3 +227,96 @@ pub mod pallet {
208227
sha2::Sha256::digest(data).into()
209228
}
210229
}
230+
231+
/// Prevalidates blob limits
232+
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
233+
#[scale_info(skip_type_params(T))]
234+
pub struct PrevalidateBlobs<T>(sp_std::marker::PhantomData<T>);
235+
236+
impl<T> PrevalidateBlobs<T> {
237+
/// Create new `SignedExtension` to prevalidate blob sizes.
238+
pub fn new() -> Self {
239+
Self(sp_std::marker::PhantomData)
240+
}
241+
}
242+
243+
impl<T> sp_std::fmt::Debug for PrevalidateBlobs<T> {
244+
#[cfg(feature = "std")]
245+
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
246+
write!(f, "PrevalidateBlobs")
247+
}
248+
249+
#[cfg(not(feature = "std"))]
250+
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
251+
Ok(())
252+
}
253+
}
254+
255+
impl<T: Config + Send + Sync> SignedExtension for PrevalidateBlobs<T>
256+
where
257+
<T as frame_system::Config>::RuntimeCall: IsSubType<Call<T>>,
258+
{
259+
type AccountId = T::AccountId;
260+
type Call = <T as frame_system::Config>::RuntimeCall;
261+
type AdditionalSigned = ();
262+
type Pre = ();
263+
const IDENTIFIER: &'static str = "PrevalidateBlobs";
264+
265+
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
266+
Ok(())
267+
}
268+
269+
fn pre_dispatch(
270+
self,
271+
who: &Self::AccountId,
272+
call: &Self::Call,
273+
info: &DispatchInfoOf<Self::Call>,
274+
len: usize,
275+
) -> Result<Self::Pre, TransactionValidityError> {
276+
// This is what's called prior to dispatching within an actual block.
277+
278+
// Check individual blob size limits
279+
self.validate(who, call, info, len)?;
280+
281+
// Here, we return `ExhaustsResources` if the total amount or size of blobs
282+
// within a block is disrespected.
283+
//
284+
// This will cause honest nodes authoring blocks to skip the transaction without
285+
// expunging it from their transaction pool.
286+
if let Some(local_call) = call.is_sub_type() {
287+
if let Call::submit_blob { blob, .. } = local_call {
288+
if TotalBlobs::<T>::get() + 1 > T::MaxBlobs::get() {
289+
return Err(InvalidTransaction::ExhaustsResources.into());
290+
}
291+
292+
if TotalBlobsSize::<T>::get() + blob.len() as u32 > T::MaxTotalBlobSize::get() {
293+
return Err(InvalidTransaction::ExhaustsResources.into());
294+
}
295+
}
296+
}
297+
298+
Ok(())
299+
}
300+
301+
fn validate(
302+
&self,
303+
_who: &Self::AccountId,
304+
call: &Self::Call,
305+
_info: &DispatchInfoOf<Self::Call>,
306+
_len: usize,
307+
) -> TransactionValidity {
308+
// This is what's called when evaluating transactions within the pool.
309+
310+
if let Some(local_call) = call.is_sub_type() {
311+
if let Call::submit_blob { blob, .. } = local_call {
312+
if blob.len() as u32 > T::MaxBlobSize::get() {
313+
// This causes the transaction to be expunged from the transaction pool.
314+
// It will not be valid unless the configured limit is increased by governance,
315+
// which is a rare event.
316+
return Err(InvalidTransaction::Custom(0).into());
317+
}
318+
}
319+
}
320+
Ok(ValidTransaction::default())
321+
}
322+
}

sugondat-chain/runtimes/test/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub type SignedExtra = (
9090
frame_system::CheckNonce<Runtime>,
9191
frame_system::CheckWeight<Runtime>,
9292
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
93+
pallet_sugondat_blobs::PrevalidateBlobs,
9394
);
9495

9596
/// Unchecked extrinsic type as expected by this runtime.

sugondat-shim/src/dock/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use subxt_signer::sr25519::Keypair;
77
use crate::sugondat_rpc;
88

99
mod rollkit;
10-
mod sovereign;
1110
mod rpc_error;
11+
mod sovereign;
1212

1313
/// A configuration for initializing all docks.
1414
pub struct Config {

0 commit comments

Comments
 (0)