1use alloc::borrow::Cow;
21use codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
22use scale_info::TypeInfo;
23use sp_runtime::{
24 traits::{ConstU32, Hash},
25 DispatchError,
26};
27use Debug;
28
29pub type BoundedInline = crate::BoundedVec<u8, ConstU32<128>>;
30
31const MAX_LEGACY_LEN: u32 = 1_000_000;
33
34#[derive(
35 Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, Clone, Eq, PartialEq, TypeInfo, Debug,
36)]
37#[codec(mel_bound())]
38pub enum Bounded<T, H: Hash> {
39 Legacy { hash: H::Output, dummy: core::marker::PhantomData<T> },
43 Inline(BoundedInline),
45 Lookup { hash: H::Output, len: u32 },
47}
48
49impl<T, H: Hash> Bounded<T, H> {
50 pub fn transmute<S: Encode>(self) -> Bounded<S, H>
61 where
62 T: Encode + EncodeLike<S>,
63 {
64 use Bounded::*;
65 match self {
66 Legacy { hash, .. } => Legacy { hash, dummy: core::marker::PhantomData },
67 Inline(x) => Inline(x),
68 Lookup { hash, len } => Lookup { hash, len },
69 }
70 }
71
72 pub fn hash(&self) -> H::Output {
76 use Bounded::*;
77 match self {
78 Lookup { hash, .. } | Legacy { hash, .. } => *hash,
79 Inline(x) => <H as Hash>::hash(x.as_ref()),
80 }
81 }
82
83 pub fn lookup_hash(&self) -> Option<H::Output> {
87 use Bounded::*;
88 match self {
89 Lookup { hash, .. } | Legacy { hash, .. } => Some(*hash),
90 Inline(_) => None,
91 }
92 }
93
94 pub fn len(&self) -> Option<u32> {
96 match self {
97 Self::Legacy { .. } => None,
98 Self::Inline(i) => Some(i.len() as u32),
99 Self::Lookup { len, .. } => Some(*len),
100 }
101 }
102
103 pub fn lookup_needed(&self) -> bool {
105 match self {
106 Self::Inline(..) => false,
107 Self::Legacy { .. } | Self::Lookup { .. } => true,
108 }
109 }
110
111 pub fn lookup_len(&self) -> Option<u32> {
113 match self {
114 Self::Inline(..) => None,
115 Self::Legacy { .. } => Some(MAX_LEGACY_LEN),
116 Self::Lookup { len, .. } => Some(*len),
117 }
118 }
119
120 pub fn unrequested(hash: H::Output, len: u32) -> Self {
122 Self::Lookup { hash, len }
123 }
124
125 #[deprecated = "This API is only for transitioning to Scheduler v3 API"]
127 pub fn from_legacy_hash(hash: impl Into<H::Output>) -> Self {
128 Self::Legacy { hash: hash.into(), dummy: core::marker::PhantomData }
129 }
130}
131
132pub type FetchResult = Result<Cow<'static, [u8]>, DispatchError>;
133
134pub trait QueryPreimage {
136 type H: Hash;
138
139 fn len(hash: &<Self::H as sp_core::Hasher>::Out) -> Option<u32>;
141
142 fn fetch(hash: &<Self::H as sp_core::Hasher>::Out, len: Option<u32>) -> FetchResult;
144
145 fn is_requested(hash: &<Self::H as sp_core::Hasher>::Out) -> bool;
147
148 fn request(hash: &<Self::H as sp_core::Hasher>::Out);
151
152 fn unrequest(hash: &<Self::H as sp_core::Hasher>::Out);
154
155 fn hold<T>(bounded: &Bounded<T, Self::H>) {
157 use Bounded::*;
158 match bounded {
159 Inline(..) => {},
160 Legacy { hash, .. } | Lookup { hash, .. } => Self::request(hash),
161 }
162 }
163
164 fn drop<T>(bounded: &Bounded<T, Self::H>) {
167 use Bounded::*;
168 match bounded {
169 Inline(..) => {},
170 Legacy { hash, .. } | Lookup { hash, .. } => Self::unrequest(hash),
171 }
172 }
173
174 fn have<T>(bounded: &Bounded<T, Self::H>) -> bool {
177 use Bounded::*;
178 match bounded {
179 Inline(..) => true,
180 Legacy { hash, .. } | Lookup { hash, .. } => Self::len(hash).is_some(),
181 }
182 }
183
184 fn pick<T>(hash: <Self::H as sp_core::Hasher>::Out, len: u32) -> Bounded<T, Self::H> {
190 Self::request(&hash);
191 Bounded::Lookup { hash, len }
192 }
193
194 fn peek<T: Decode>(bounded: &Bounded<T, Self::H>) -> Result<(T, Option<u32>), DispatchError> {
200 use Bounded::*;
201 match bounded {
202 Inline(data) => T::decode(&mut &data[..]).ok().map(|x| (x, None)),
203 Lookup { hash, len } => {
204 let data = Self::fetch(hash, Some(*len))?;
205 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
206 },
207 Legacy { hash, .. } => {
208 let data = Self::fetch(hash, None)?;
209 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
210 },
211 }
212 .ok_or(DispatchError::Corruption)
213 }
214
215 fn realize<T: Decode>(
219 bounded: &Bounded<T, Self::H>,
220 ) -> Result<(T, Option<u32>), DispatchError> {
221 let r = Self::peek(bounded)?;
222 Self::drop(bounded);
223 Ok(r)
224 }
225}
226
227pub trait StorePreimage: QueryPreimage {
233 const MAX_LENGTH: usize;
237
238 fn note(bytes: Cow<[u8]>) -> Result<<Self::H as sp_core::Hasher>::Out, DispatchError>;
242
243 fn unnote(hash: &<Self::H as sp_core::Hasher>::Out) {
246 Self::unrequest(hash)
247 }
248
249 fn bound<T: Encode>(t: T) -> Result<Bounded<T, Self::H>, DispatchError> {
256 let data = t.encode();
257 let len = data.len() as u32;
258 Ok(match BoundedInline::try_from(data) {
259 Ok(bounded) => Bounded::Inline(bounded),
260 Err(unbounded) => Bounded::Lookup { hash: Self::note(unbounded.into())?, len },
261 })
262 }
263}
264
265impl QueryPreimage for () {
266 type H = sp_runtime::traits::BlakeTwo256;
267
268 fn len(_: &sp_core::H256) -> Option<u32> {
269 None
270 }
271 fn fetch(_: &sp_core::H256, _: Option<u32>) -> FetchResult {
272 Err(DispatchError::Unavailable)
273 }
274 fn is_requested(_: &sp_core::H256) -> bool {
275 false
276 }
277 fn request(_: &sp_core::H256) {}
278 fn unrequest(_: &sp_core::H256) {}
279}
280
281impl StorePreimage for () {
282 const MAX_LENGTH: usize = 0;
283 fn note(_: Cow<[u8]>) -> Result<sp_core::H256, DispatchError> {
284 Err(DispatchError::Exhausted)
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use crate::BoundedVec;
292 use sp_runtime::{bounded_vec, traits::BlakeTwo256};
293
294 #[test]
295 fn bounded_size_is_correct() {
296 assert_eq!(<Bounded<Vec<u8>, BlakeTwo256> as MaxEncodedLen>::max_encoded_len(), 131);
297 }
298
299 #[test]
300 fn bounded_basic_works() {
301 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
302 let len = data.len() as u32;
303 let hash = BlakeTwo256::hash(&data).into();
304
305 {
307 let bound: Bounded<Vec<u8>, BlakeTwo256> = Bounded::Inline(data.clone());
308 assert_eq!(bound.hash(), hash);
309 assert_eq!(bound.len(), Some(len));
310 assert!(!bound.lookup_needed());
311 assert_eq!(bound.lookup_len(), None);
312 }
313 {
315 let bound: Bounded<Vec<u8>, BlakeTwo256> =
316 Bounded::Legacy { hash, dummy: Default::default() };
317 assert_eq!(bound.hash(), hash);
318 assert_eq!(bound.len(), None);
319 assert!(bound.lookup_needed());
320 assert_eq!(bound.lookup_len(), Some(1_000_000));
321 }
322 {
324 let bound: Bounded<Vec<u8>, BlakeTwo256> =
325 Bounded::Lookup { hash, len: data.len() as u32 };
326 assert_eq!(bound.hash(), hash);
327 assert_eq!(bound.len(), Some(len));
328 assert!(bound.lookup_needed());
329 assert_eq!(bound.lookup_len(), Some(len));
330 }
331 }
332
333 #[test]
334 fn bounded_transmuting_works() {
335 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
336
337 let x: Bounded<String, BlakeTwo256> = Bounded::Inline(data.clone());
339 let y: Bounded<&str, BlakeTwo256> = x.transmute();
340 assert_eq!(y, Bounded::Inline(data));
341 }
342}