1use alloc::borrow::Cow;
21use codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
22use scale_info::TypeInfo;
23use sp_core::RuntimeDebug;
24use sp_runtime::{
25 traits::{ConstU32, Hash},
26 DispatchError,
27};
28
29pub type BoundedInline = crate::BoundedVec<u8, ConstU32<128>>;
30
31const MAX_LEGACY_LEN: u32 = 1_000_000;
33
34#[derive(
35 Encode,
36 Decode,
37 DecodeWithMemTracking,
38 MaxEncodedLen,
39 Clone,
40 Eq,
41 PartialEq,
42 TypeInfo,
43 RuntimeDebug,
44)]
45#[codec(mel_bound())]
46pub enum Bounded<T, H: Hash> {
47 Legacy { hash: H::Output, dummy: core::marker::PhantomData<T> },
51 Inline(BoundedInline),
53 Lookup { hash: H::Output, len: u32 },
55}
56
57impl<T, H: Hash> Bounded<T, H> {
58 pub fn transmute<S: Encode>(self) -> Bounded<S, H>
69 where
70 T: Encode + EncodeLike<S>,
71 {
72 use Bounded::*;
73 match self {
74 Legacy { hash, .. } => Legacy { hash, dummy: core::marker::PhantomData },
75 Inline(x) => Inline(x),
76 Lookup { hash, len } => Lookup { hash, len },
77 }
78 }
79
80 pub fn hash(&self) -> H::Output {
84 use Bounded::*;
85 match self {
86 Lookup { hash, .. } | Legacy { hash, .. } => *hash,
87 Inline(x) => <H as Hash>::hash(x.as_ref()),
88 }
89 }
90
91 pub fn lookup_hash(&self) -> Option<H::Output> {
95 use Bounded::*;
96 match self {
97 Lookup { hash, .. } | Legacy { hash, .. } => Some(*hash),
98 Inline(_) => None,
99 }
100 }
101
102 pub fn len(&self) -> Option<u32> {
104 match self {
105 Self::Legacy { .. } => None,
106 Self::Inline(i) => Some(i.len() as u32),
107 Self::Lookup { len, .. } => Some(*len),
108 }
109 }
110
111 pub fn lookup_needed(&self) -> bool {
113 match self {
114 Self::Inline(..) => false,
115 Self::Legacy { .. } | Self::Lookup { .. } => true,
116 }
117 }
118
119 pub fn lookup_len(&self) -> Option<u32> {
121 match self {
122 Self::Inline(..) => None,
123 Self::Legacy { .. } => Some(MAX_LEGACY_LEN),
124 Self::Lookup { len, .. } => Some(*len),
125 }
126 }
127
128 pub fn unrequested(hash: H::Output, len: u32) -> Self {
130 Self::Lookup { hash, len }
131 }
132
133 #[deprecated = "This API is only for transitioning to Scheduler v3 API"]
135 pub fn from_legacy_hash(hash: impl Into<H::Output>) -> Self {
136 Self::Legacy { hash: hash.into(), dummy: core::marker::PhantomData }
137 }
138}
139
140pub type FetchResult = Result<Cow<'static, [u8]>, DispatchError>;
141
142pub trait QueryPreimage {
144 type H: Hash;
146
147 fn len(hash: &<Self::H as sp_core::Hasher>::Out) -> Option<u32>;
149
150 fn fetch(hash: &<Self::H as sp_core::Hasher>::Out, len: Option<u32>) -> FetchResult;
152
153 fn is_requested(hash: &<Self::H as sp_core::Hasher>::Out) -> bool;
155
156 fn request(hash: &<Self::H as sp_core::Hasher>::Out);
159
160 fn unrequest(hash: &<Self::H as sp_core::Hasher>::Out);
162
163 fn hold<T>(bounded: &Bounded<T, Self::H>) {
165 use Bounded::*;
166 match bounded {
167 Inline(..) => {},
168 Legacy { hash, .. } | Lookup { hash, .. } => Self::request(hash),
169 }
170 }
171
172 fn drop<T>(bounded: &Bounded<T, Self::H>) {
175 use Bounded::*;
176 match bounded {
177 Inline(..) => {},
178 Legacy { hash, .. } | Lookup { hash, .. } => Self::unrequest(hash),
179 }
180 }
181
182 fn have<T>(bounded: &Bounded<T, Self::H>) -> bool {
185 use Bounded::*;
186 match bounded {
187 Inline(..) => true,
188 Legacy { hash, .. } | Lookup { hash, .. } => Self::len(hash).is_some(),
189 }
190 }
191
192 fn pick<T>(hash: <Self::H as sp_core::Hasher>::Out, len: u32) -> Bounded<T, Self::H> {
198 Self::request(&hash);
199 Bounded::Lookup { hash, len }
200 }
201
202 fn peek<T: Decode>(bounded: &Bounded<T, Self::H>) -> Result<(T, Option<u32>), DispatchError> {
208 use Bounded::*;
209 match bounded {
210 Inline(data) => T::decode(&mut &data[..]).ok().map(|x| (x, None)),
211 Lookup { hash, len } => {
212 let data = Self::fetch(hash, Some(*len))?;
213 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
214 },
215 Legacy { hash, .. } => {
216 let data = Self::fetch(hash, None)?;
217 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
218 },
219 }
220 .ok_or(DispatchError::Corruption)
221 }
222
223 fn realize<T: Decode>(
227 bounded: &Bounded<T, Self::H>,
228 ) -> Result<(T, Option<u32>), DispatchError> {
229 let r = Self::peek(bounded)?;
230 Self::drop(bounded);
231 Ok(r)
232 }
233}
234
235pub trait StorePreimage: QueryPreimage {
241 const MAX_LENGTH: usize;
245
246 fn note(bytes: Cow<[u8]>) -> Result<<Self::H as sp_core::Hasher>::Out, DispatchError>;
250
251 fn unnote(hash: &<Self::H as sp_core::Hasher>::Out) {
254 Self::unrequest(hash)
255 }
256
257 fn bound<T: Encode>(t: T) -> Result<Bounded<T, Self::H>, DispatchError> {
264 let data = t.encode();
265 let len = data.len() as u32;
266 Ok(match BoundedInline::try_from(data) {
267 Ok(bounded) => Bounded::Inline(bounded),
268 Err(unbounded) => Bounded::Lookup { hash: Self::note(unbounded.into())?, len },
269 })
270 }
271}
272
273impl QueryPreimage for () {
274 type H = sp_runtime::traits::BlakeTwo256;
275
276 fn len(_: &sp_core::H256) -> Option<u32> {
277 None
278 }
279 fn fetch(_: &sp_core::H256, _: Option<u32>) -> FetchResult {
280 Err(DispatchError::Unavailable)
281 }
282 fn is_requested(_: &sp_core::H256) -> bool {
283 false
284 }
285 fn request(_: &sp_core::H256) {}
286 fn unrequest(_: &sp_core::H256) {}
287}
288
289impl StorePreimage for () {
290 const MAX_LENGTH: usize = 0;
291 fn note(_: Cow<[u8]>) -> Result<sp_core::H256, DispatchError> {
292 Err(DispatchError::Exhausted)
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use crate::BoundedVec;
300 use sp_runtime::{bounded_vec, traits::BlakeTwo256};
301
302 #[test]
303 fn bounded_size_is_correct() {
304 assert_eq!(<Bounded<Vec<u8>, BlakeTwo256> as MaxEncodedLen>::max_encoded_len(), 131);
305 }
306
307 #[test]
308 fn bounded_basic_works() {
309 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
310 let len = data.len() as u32;
311 let hash = BlakeTwo256::hash(&data).into();
312
313 {
315 let bound: Bounded<Vec<u8>, BlakeTwo256> = Bounded::Inline(data.clone());
316 assert_eq!(bound.hash(), hash);
317 assert_eq!(bound.len(), Some(len));
318 assert!(!bound.lookup_needed());
319 assert_eq!(bound.lookup_len(), None);
320 }
321 {
323 let bound: Bounded<Vec<u8>, BlakeTwo256> =
324 Bounded::Legacy { hash, dummy: Default::default() };
325 assert_eq!(bound.hash(), hash);
326 assert_eq!(bound.len(), None);
327 assert!(bound.lookup_needed());
328 assert_eq!(bound.lookup_len(), Some(1_000_000));
329 }
330 {
332 let bound: Bounded<Vec<u8>, BlakeTwo256> =
333 Bounded::Lookup { hash, len: data.len() as u32 };
334 assert_eq!(bound.hash(), hash);
335 assert_eq!(bound.len(), Some(len));
336 assert!(bound.lookup_needed());
337 assert_eq!(bound.lookup_len(), Some(len));
338 }
339 }
340
341 #[test]
342 fn bounded_transmuting_works() {
343 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
344
345 let x: Bounded<String, BlakeTwo256> = Bounded::Inline(data.clone());
347 let y: Bounded<&str, BlakeTwo256> = x.transmute();
348 assert_eq!(y, Bounded::Inline(data));
349 }
350}