Skip to content

Commit b472feb

Browse files
gabriele-0201pepyakin
authored andcommitted
runtime/blobs: add tests
1 parent 7f96c3c commit b472feb

File tree

2 files changed

+284
-5
lines changed

2 files changed

+284
-5
lines changed

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub mod pallet {
5757
pub type TotalBlobs<T: Config> = StorageValue<_, u32, ValueQuery>;
5858

5959
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Clone)]
60+
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
6061
pub struct SubmittedBlobMetadata<AccountId> {
6162
pub who: AccountId,
6263
pub extrinsic_index: u32,
@@ -163,11 +164,6 @@ pub mod pallet {
163164
) -> DispatchResultWithPostInfo {
164165
let who = ensure_signed(origin)?;
165166

166-
let blob_len = blob.len() as u32;
167-
if blob_len > T::MaxBlobSize::get() {
168-
return Err(Error::<T>::BlobTooLarge.into());
169-
}
170-
171167
let Some(extrinsic_index) = <frame_system::Pallet<T>>::extrinsic_index() else {
172168
return Err(Error::<T>::NoExtrinsicIndex.into());
173169
};
@@ -178,6 +174,7 @@ pub mod pallet {
178174
}
179175
TotalBlobs::<T>::put(total_blobs + 1);
180176

177+
let blob_len = blob.len() as u32;
181178
let total_blobs_size = TotalBlobsSize::<T>::get();
182179
if total_blobs_size + blob_len > T::MaxTotalBlobSize::get() {
183180
return Err(Error::<T>::MaxTotalBlobsSizeReached.into());
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
use crate as pallet_blobs;
2+
use crate::{mock::*, *};
3+
use codec::Encode;
4+
use frame_support::traits::Hooks;
5+
use frame_support::{assert_noop, assert_ok, traits::Get, BoundedVec};
6+
use sha2::Digest;
7+
use sp_core::{crypto::Pair, sr25519};
8+
use sugondat_nmt::{Namespace, NmtLeaf};
9+
10+
fn get_blob(size: u32) -> BoundedVec<u8, <Test as pallet_blobs::Config>::MaxBlobSize> {
11+
vec![12u8]
12+
.repeat(size as usize)
13+
.try_into()
14+
.expect("provided size biggger then MaxBlobSize")
15+
}
16+
17+
fn alice() -> <Test as frame_system::Config>::AccountId {
18+
sr25519::Pair::from_string("//Alice", None)
19+
.expect("Impossible generate Alice AccountId")
20+
.public()
21+
.into()
22+
}
23+
24+
#[test]
25+
fn test_correct_submitted_blob() {
26+
new_test_ext().execute_with(|| {
27+
let blob_len = 1024;
28+
let blob = get_blob(blob_len);
29+
let namespace_id = 0;
30+
31+
assert_ok!(Blobs::submit_blob(
32+
RuntimeOrigin::signed(alice()),
33+
namespace_id,
34+
blob.clone()
35+
));
36+
37+
assert_eq!(TotalBlobs::<Test>::get(), 1);
38+
assert_eq!(TotalBlobsSize::<Test>::get(), blob_len);
39+
let blob_list = SubmittedBlobMetadata {
40+
who: alice(),
41+
extrinsic_index: 0,
42+
namespace_id,
43+
blob_hash: sha2::Sha256::digest(blob).into(),
44+
};
45+
assert_eq!(BlobList::<Test>::get().to_vec(), vec![blob_list]);
46+
});
47+
}
48+
49+
#[test]
50+
fn test_no_extrinsic_index() {
51+
new_test_ext().execute_with(|| {
52+
sp_io::storage::clear(b":extrinsic_index");
53+
assert_noop!(
54+
Blobs::submit_blob(RuntimeOrigin::signed(alice()), 0, get_blob(10)),
55+
Error::<Test>::NoExtrinsicIndex
56+
);
57+
});
58+
}
59+
60+
#[test]
61+
fn test_max_blobs_reached() {
62+
let max_blobs: u32 = <Test as pallet_blobs::Config>::MaxBlobs::get();
63+
64+
new_test_ext().execute_with(|| {
65+
let blob = get_blob(1);
66+
for i in 0..max_blobs {
67+
assert_ok!(Blobs::submit_blob(
68+
RuntimeOrigin::signed(alice()),
69+
i,
70+
blob.clone()
71+
));
72+
}
73+
assert_noop!(
74+
Blobs::submit_blob(RuntimeOrigin::signed(alice()), 0, blob.clone()),
75+
Error::<Test>::MaxBlobsReached
76+
);
77+
});
78+
}
79+
80+
#[test]
81+
fn test_max_total_blob_size() {
82+
let max_total_blobs_size: u32 = <Test as pallet_blobs::Config>::MaxTotalBlobSize::get();
83+
let max_blob_size: u32 = <Test as pallet_blobs::Config>::MaxBlobSize::get();
84+
let blobs_needed = max_total_blobs_size / max_blob_size;
85+
86+
new_test_ext().execute_with(|| {
87+
let blob = get_blob(max_blob_size);
88+
for i in 0..blobs_needed {
89+
assert_ok!(Blobs::submit_blob(
90+
RuntimeOrigin::signed(alice()),
91+
i,
92+
blob.clone()
93+
));
94+
}
95+
assert_noop!(
96+
Blobs::submit_blob(RuntimeOrigin::signed(alice()), 0, blob.clone()),
97+
Error::<Test>::MaxTotalBlobsSizeReached
98+
);
99+
});
100+
}
101+
102+
#[test]
103+
fn test_blob_appended_to_blob_list() {
104+
new_test_ext().execute_with(|| {
105+
let blob_len = 1024;
106+
let blob = get_blob(blob_len);
107+
let blob_hash: [u8; 32] = sha2::Sha256::digest(blob.clone()).into();
108+
let mut blobs_metadata = vec![];
109+
110+
let mut submit_blob_and_assert = |namespace_id, extrinsic_index: u32| {
111+
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
112+
assert_ok!(Blobs::submit_blob(
113+
RuntimeOrigin::signed(alice()),
114+
namespace_id,
115+
blob.clone()
116+
));
117+
118+
blobs_metadata.push(SubmittedBlobMetadata {
119+
who: alice(),
120+
extrinsic_index,
121+
namespace_id,
122+
blob_hash: blob_hash.clone(),
123+
});
124+
125+
assert_eq!(BlobList::<Test>::get().to_vec(), blobs_metadata);
126+
};
127+
128+
submit_blob_and_assert(1, 0);
129+
submit_blob_and_assert(3, 1);
130+
submit_blob_and_assert(2, 2);
131+
});
132+
}
133+
134+
#[test]
135+
fn test_namespace_order() {
136+
new_test_ext().execute_with(|| {
137+
let blob_len = 1024;
138+
let blob = get_blob(blob_len);
139+
let blob_hash: [u8; 32] = sha2::Sha256::digest(blob.clone()).into();
140+
141+
let mut tree = sugondat_nmt::TreeBuilder::new();
142+
let mut blobs_metadata = vec![];
143+
144+
let mut push_leaf = |namespace_id, extrinsic_index| {
145+
tree.push_leaf(
146+
Namespace::from_u32_be(namespace_id),
147+
NmtLeaf {
148+
extrinsic_index,
149+
who: alice().into(),
150+
blob_hash: blob_hash.clone(),
151+
},
152+
)
153+
.expect("Impossible push leaf into nmt-tree");
154+
};
155+
156+
let mut submit_blob = |namespace_id, extrinsic_index: u32| {
157+
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
158+
assert_ok!(Blobs::submit_blob(
159+
RuntimeOrigin::signed(alice()),
160+
namespace_id,
161+
blob.clone()
162+
));
163+
164+
blobs_metadata.push(SubmittedBlobMetadata {
165+
who: alice(),
166+
extrinsic_index,
167+
namespace_id,
168+
blob_hash: blob_hash.clone(),
169+
});
170+
};
171+
172+
push_leaf(1, 3);
173+
push_leaf(1 << 8, 2);
174+
push_leaf(1 << 16, 1);
175+
push_leaf(1 << 24, 0);
176+
177+
submit_blob(1 << 24, 0);
178+
submit_blob(1 << 16, 1);
179+
submit_blob(1 << 8, 2);
180+
submit_blob(1, 3);
181+
182+
assert_eq!(TotalBlobsSize::<Test>::get(), blob_len * 4);
183+
assert_eq!(BlobList::<Test>::get().to_vec(), blobs_metadata);
184+
185+
Blobs::on_finalize(System::block_number());
186+
187+
let mut logs = System::digest().logs.into_iter();
188+
match logs.next() {
189+
Some(sp_runtime::DigestItem::Other(bytes)) if bytes.starts_with(b"snmt") => {
190+
assert_eq!(bytes[4..], tree.root().to_raw_bytes());
191+
}
192+
_ => panic!("One DigestItem::Other should be contained in the Digest"),
193+
}
194+
// No other logs are expected
195+
assert_eq!(None, logs.next());
196+
});
197+
}
198+
199+
#[test]
200+
fn test_deposited_event() {
201+
new_test_ext().execute_with(|| {
202+
let blob_len = 50;
203+
let blob = get_blob(blob_len);
204+
let blob_hash = sha2::Sha256::digest(blob.clone()).into();
205+
let namespace_id = 9;
206+
let extrinsic_index = 15;
207+
208+
// First block produced will not produce events so set the block number to 1
209+
System::set_block_number(1);
210+
211+
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
212+
213+
assert_ok!(Blobs::submit_blob(
214+
RuntimeOrigin::signed(alice()),
215+
namespace_id,
216+
blob.clone()
217+
));
218+
219+
let event = Event::<Test>::BlobStored {
220+
who: alice(),
221+
extrinsic_index,
222+
namespace_id,
223+
blob_len,
224+
blob_hash,
225+
};
226+
227+
System::assert_last_event(event.into());
228+
});
229+
}
230+
231+
#[test]
232+
fn test_on_finalize() {
233+
use sha2::Digest;
234+
use sugondat_nmt::TreeBuilder;
235+
236+
let max_blobs: u32 = <Test as pallet_blobs::Config>::MaxBlobs::get();
237+
let mut tree = TreeBuilder::new();
238+
let blob = get_blob(1);
239+
// Counter to avoid recreating the tree from scratch everytime the loop restarts
240+
let mut added_leaf = 0;
241+
242+
// Try on finalize 10 times
243+
for n_blob_to_test in (0..max_blobs).step_by((max_blobs / 10) as usize) {
244+
for extrinsic_index in added_leaf..n_blob_to_test {
245+
tree.push_leaf(
246+
Namespace::from_u32_be(extrinsic_index),
247+
NmtLeaf {
248+
extrinsic_index,
249+
who: alice().into(),
250+
blob_hash: sha2::Sha256::digest(blob.clone()).into(),
251+
},
252+
)
253+
.expect("Impossible push leaf into nmt-tree");
254+
}
255+
added_leaf = n_blob_to_test;
256+
257+
new_test_ext().execute_with(|| {
258+
// prepare the state
259+
for extrinsic_index in 0..n_blob_to_test {
260+
sp_io::storage::set(b":extrinsic_index", &(extrinsic_index).encode());
261+
assert_ok!(Blobs::submit_blob(
262+
RuntimeOrigin::signed(alice()),
263+
extrinsic_index,
264+
blob.clone()
265+
));
266+
}
267+
268+
// Call on finalize and theck the deposited nmt root in the last event is correct
269+
Blobs::on_finalize(System::block_number());
270+
271+
let mut logs = System::digest().logs.into_iter();
272+
match logs.next() {
273+
Some(sp_runtime::DigestItem::Other(bytes)) if bytes.starts_with(b"snmt") => {
274+
assert_eq!(bytes[4..], tree.root().to_raw_bytes());
275+
}
276+
_ => panic!("One DigestItem::Other should be contained in the Digest"),
277+
}
278+
// No other logs are expected
279+
assert_eq!(None, logs.next());
280+
});
281+
}
282+
}

0 commit comments

Comments
 (0)