Skip to content

Commit 40d6b1a

Browse files
committed
add namespace type to primitives
1 parent 793ffe1 commit 40d6b1a

File tree

12 files changed

+211
-50
lines changed

12 files changed

+211
-50
lines changed

Cargo.lock

Lines changed: 36 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sugondat-chain/pallets/blobs/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ sugondat-nmt = { path = "../../../sugondat-nmt", default-features = false }
2929

3030
[dev-dependencies]
3131
serde = { version = "1.0.132" }
32-
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
32+
quickcheck = "1.0.3"
33+
quickcheck_macros = "1.0.0"
3334

3435
# Substrate
3536
sp-core = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
37+
sp-io = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0"}
3638

3739
[features]
3840
default = ["std"]

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
/// <https://docs.substrate.io/v3/runtime/frame>
66
pub use pallet::*;
77

8+
mod namespace_param;
9+
810
#[cfg(test)]
911
mod mock;
1012
#[cfg(test)]
@@ -28,6 +30,7 @@ use frame_support::traits::{Get, IsSubType};
2830

2931
#[frame_support::pallet]
3032
pub mod pallet {
33+
use crate::namespace_param::UnvalidatedNamespace;
3134
pub use crate::weights::WeightInfo;
3235
use frame_support::{
3336
dispatch::DispatchResultWithPostInfo,
@@ -74,7 +77,7 @@ pub mod pallet {
7477
pub struct SubmittedBlobMetadata<AccountId> {
7578
pub who: AccountId,
7679
pub extrinsic_index: u32,
77-
pub namespace_id: u32,
80+
pub namespace_id: u128,
7881
pub blob_hash: [u8; 32],
7982
}
8083

@@ -95,7 +98,7 @@ pub mod pallet {
9598
/// The extrinsic index at which the blob was submitted.
9699
extrinsic_index: u32,
97100
/// The namespace ID the blob was submitted in.
98-
namespace_id: u32,
101+
namespace_id: u128,
99102
/// The length of the blob data.
100103
blob_len: u32,
101104
/// The SHA256 hash of the blob.
@@ -137,7 +140,7 @@ pub mod pallet {
137140
let blobs = BlobList::<T>::take()
138141
.iter()
139142
.map(|blob| sugondat_nmt::BlobMetadata {
140-
namespace: sugondat_nmt::Namespace::from_u32_be(blob.namespace_id),
143+
namespace: sugondat_nmt::Namespace::from_u128_be(blob.namespace_id),
141144
leaf: sugondat_nmt::NmtLeaf {
142145
extrinsic_index: blob.extrinsic_index,
143146
who: blob.who.encode().try_into().unwrap(),
@@ -170,11 +173,16 @@ pub mod pallet {
170173
)]
171174
pub fn submit_blob(
172175
origin: OriginFor<T>,
173-
namespace_id: u32,
176+
namespace_id: UnvalidatedNamespace,
174177
blob: Vec<u8>,
175178
) -> DispatchResultWithPostInfo {
176179
let who = ensure_signed(origin)?;
177180

181+
let namespace_id = match namespace_id.validate() {
182+
Ok(id) => id,
183+
Err(_) => panic!("Invalid namespace"),
184+
};
185+
178186
let blob_len = blob.len() as u32;
179187
if blob_len > T::MaxBlobSize::get() {
180188
panic!("Blob size limit exceeded");
@@ -304,11 +312,24 @@ where
304312
// This is what's called when evaluating transactions within the pool.
305313

306314
if let Some(local_call) = call.is_sub_type() {
307-
if let Call::submit_blob { blob, .. } = local_call {
315+
// Failures here are intended to expunge the transaction from the pool
316+
// entirely.
317+
if let Call::submit_blob {
318+
namespace_id, blob, ..
319+
} = local_call
320+
{
321+
if namespace_id.validate().is_err() {
322+
// This could become valid later when accepting new namespace ID
323+
// formats, but that is rare.
324+
return Err(InvalidTransaction::Custom(
325+
InvalidTransactionCustomError::InvalidNamespaceId as u8,
326+
)
327+
.into());
328+
}
329+
308330
if blob.len() as u32 > T::MaxBlobSize::get() {
309-
// This causes the transaction to be expunged from the transaction pool.
310-
// It will not be valid unless the configured limit is increased by governance,
311-
// which is a rare event.
331+
// This could become valid later, but only if the configured limit is
332+
// increased by governance, which is a rare event.
312333
return Err(InvalidTransaction::Custom(
313334
InvalidTransactionCustomError::BlobExceedsSizeLimit as u8,
314335
)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Namespaces as a parameter.
2+
//!
3+
//! Namespaces are encoded as 16-byte arrays, with the following schema:
4+
//! - The first byte is reserved for a version byte which determines the format
5+
//! of the following 15 bytes. At the moment, the only supported value for this byte
6+
//! is `0x00`, which indicates version 0.
7+
//! - In version 0, bytes 1 through 5 are required to be equal to `0x00` and bytes 6 through
8+
//! 15 are allowed to hold any value.
9+
10+
use codec::{Decode, Encode, MaxEncodedLen};
11+
use scale_info::TypeInfo;
12+
use sp_runtime::RuntimeDebug;
13+
14+
/// An error in namespace validation.
15+
pub enum NamespaceValidationError {
16+
/// Unrecognized version.
17+
UnrecognizedVersion(u8),
18+
/// V0: reserved bytes are non-zero.
19+
V0NonZeroReserved,
20+
}
21+
22+
/// Validate a namespace against known schemas.
23+
pub fn validate(namespace: &[u8; 16]) -> Result<(), NamespaceValidationError> {
24+
if namespace[0] != 0 {
25+
return Err(NamespaceValidationError::UnrecognizedVersion(namespace[0]));
26+
}
27+
if &namespace[1..6] != &[0, 0, 0, 0, 0] {
28+
return Err(NamespaceValidationError::V0NonZeroReserved);
29+
}
30+
31+
Ok(())
32+
}
33+
34+
/// Type-safe wrapper around an unvalidated blob namespace.
35+
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Clone, PartialEq, RuntimeDebug)]
36+
pub struct UnvalidatedNamespace([u8; 16]);
37+
38+
impl UnvalidatedNamespace {
39+
/// Validate the namespace, extracting the full data.
40+
pub fn validate(&self) -> Result<u128, NamespaceValidationError> {
41+
validate(&self.0).map(|()| u128::from_be_bytes(self.0))
42+
}
43+
}
44+
45+
impl From<[u8; 16]> for UnvalidatedNamespace {
46+
fn from(x: [u8; 16]) -> Self {
47+
UnvalidatedNamespace(x)
48+
}
49+
}
50+
51+
impl From<u128> for UnvalidatedNamespace {
52+
fn from(x: u128) -> Self {
53+
UnvalidatedNamespace(x.to_be_bytes())
54+
}
55+
}
56+
57+
#[cfg(test)]
58+
mod tests {
59+
use super::*;
60+
use quickcheck::TestResult;
61+
use quickcheck_macros::quickcheck;
62+
use std::matches;
63+
64+
#[quickcheck]
65+
fn namespace_validation_not_v0_fails(version_byte: u8) -> TestResult {
66+
if version_byte == 0x00 {
67+
return TestResult::discard();
68+
}
69+
TestResult::from_bool(matches!(
70+
validate(&[version_byte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
71+
Err(NamespaceValidationError::UnrecognizedVersion(v)) if v == version_byte,
72+
))
73+
}
74+
75+
#[quickcheck]
76+
fn namespace_validation_v0_reserved_occupied_fails(
77+
reserved: (u8, u8, u8, u8, u8),
78+
) -> TestResult {
79+
if reserved == (0, 0, 0, 0, 0) {
80+
return TestResult::discard();
81+
}
82+
let (a, b, c, d, e) = reserved;
83+
TestResult::from_bool(matches!(
84+
validate(&[0u8, a, b, c, d, e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
85+
Err(NamespaceValidationError::V0NonZeroReserved),
86+
))
87+
}
88+
89+
#[quickcheck]
90+
fn namespace_validation_v0_works(namespace: Vec<u8>) -> TestResult {
91+
if namespace.len() < 10 {
92+
return TestResult::discard();
93+
}
94+
95+
let mut n = [0u8; 16];
96+
n[6..].copy_from_slice(&namespace[..10]);
97+
TestResult::from_bool(matches!(validate(&n), Ok(())))
98+
}
99+
}

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate as pallet_blobs;
22
use crate::{mock::*, *};
33
use codec::Encode;
44
use frame_support::traits::Hooks;
5-
use frame_support::{assert_noop, assert_ok, traits::Get, BoundedVec};
5+
use frame_support::{assert_noop, assert_ok, traits::Get};
66
use sha2::Digest;
77
use sp_core::{crypto::Pair, sr25519};
88
use sp_runtime::transaction_validity::{
@@ -30,7 +30,7 @@ fn test_correct_submitted_blob() {
3030

3131
assert_ok!(Blobs::submit_blob(
3232
RuntimeOrigin::signed(alice()),
33-
namespace_id,
33+
namespace_id.into(),
3434
blob.clone()
3535
));
3636

@@ -51,7 +51,7 @@ fn test_no_extrinsic_index() {
5151
new_test_ext().execute_with(|| {
5252
sp_io::storage::clear(b":extrinsic_index");
5353
assert_noop!(
54-
Blobs::submit_blob(RuntimeOrigin::signed(alice()), 0, get_blob(10)),
54+
Blobs::submit_blob(RuntimeOrigin::signed(alice()), 0.into(), get_blob(10)),
5555
Error::<Test>::NoExtrinsicIndex
5656
);
5757
});
@@ -63,7 +63,7 @@ macro_rules! submit_blobs {
6363
for i in 0..$n_blobs {
6464
assert_ok!(Blobs::submit_blob(
6565
RuntimeOrigin::signed(alice()),
66-
i,
66+
(i as u128).into(),
6767
blob.clone()
6868
));
6969
}
@@ -118,11 +118,11 @@ fn test_blob_appended_to_blob_list() {
118118
let blob_hash: [u8; 32] = sha2::Sha256::digest(blob.clone()).into();
119119
let mut blobs_metadata = vec![];
120120

121-
let mut submit_blob_and_assert = |namespace_id, extrinsic_index: u32| {
121+
let mut submit_blob_and_assert = |namespace_id: u128, extrinsic_index: u32| {
122122
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
123123
assert_ok!(Blobs::submit_blob(
124124
RuntimeOrigin::signed(alice()),
125-
namespace_id,
125+
namespace_id.into(),
126126
blob.clone()
127127
));
128128

@@ -154,7 +154,7 @@ fn test_namespace_order() {
154154

155155
let mut push_leaf = |namespace_id, extrinsic_index| {
156156
tree.push_leaf(
157-
Namespace::from_u32_be(namespace_id),
157+
Namespace::from_u128_be(namespace_id),
158158
NmtLeaf {
159159
extrinsic_index,
160160
who: alice().into(),
@@ -164,11 +164,11 @@ fn test_namespace_order() {
164164
.expect("Impossible push leaf into nmt-tree");
165165
};
166166

167-
let mut submit_blob = |namespace_id, extrinsic_index: u32| {
167+
let mut submit_blob = |namespace_id: u128, extrinsic_index: u32| {
168168
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
169169
assert_ok!(Blobs::submit_blob(
170170
RuntimeOrigin::signed(alice()),
171-
namespace_id,
171+
namespace_id.into(),
172172
blob.clone()
173173
));
174174

@@ -223,7 +223,7 @@ fn test_deposited_event() {
223223

224224
assert_ok!(Blobs::submit_blob(
225225
RuntimeOrigin::signed(alice()),
226-
namespace_id,
226+
namespace_id.into(),
227227
blob.clone()
228228
));
229229

@@ -254,7 +254,7 @@ fn test_on_finalize() {
254254
for n_blob_to_test in (0..max_blobs).step_by((max_blobs / 10) as usize) {
255255
for extrinsic_index in added_leaf..n_blob_to_test {
256256
tree.push_leaf(
257-
Namespace::from_u32_be(extrinsic_index),
257+
Namespace::from_u128_be(extrinsic_index as u128),
258258
NmtLeaf {
259259
extrinsic_index,
260260
who: alice().into(),
@@ -271,7 +271,7 @@ fn test_on_finalize() {
271271
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
272272
assert_ok!(Blobs::submit_blob(
273273
RuntimeOrigin::signed(alice()),
274-
extrinsic_index,
274+
(extrinsic_index as u128).into(),
275275
blob.clone()
276276
));
277277
}
@@ -296,7 +296,7 @@ macro_rules! submit_blob_call {
296296
([blob_size] $blob_size: expr) => {
297297
RuntimeCall::Blobs(
298298
Call::submit_blob {
299-
namespace_id: 0,
299+
namespace_id: 0.into(),
300300
blob: get_blob($blob_size),
301301
}
302302
.into(),

sugondat-chain/primitives/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ license = "MIT OR Apache-2.0"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10+
codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"], default-features = false }
11+
1012
sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
1113
sp-core = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
1214
sp-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.4.0" }
1315

1416
[features]
1517
default = ["std"]
16-
std = ["sp-runtime/std", "sp-core/std", "sp-consensus-aura/std"]
18+
std = ["codec/std", "sp-runtime/std", "sp-core/std", "sp-consensus-aura/std"]

0 commit comments

Comments
 (0)