1use alloc::borrow::Cow;
21use codec::{Decode, 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(Encode, Decode, MaxEncodedLen, Clone, Eq, PartialEq, TypeInfo, RuntimeDebug)]
35#[codec(mel_bound())]
36pub enum Bounded<T, H: Hash> {
37 Legacy { hash: H::Output, dummy: core::marker::PhantomData<T> },
41 Inline(BoundedInline),
43 Lookup { hash: H::Output, len: u32 },
45}
46
47impl<T, H: Hash> Bounded<T, H> {
48 pub fn transmute<S: Encode>(self) -> Bounded<S, H>
59 where
60 T: Encode + EncodeLike<S>,
61 {
62 use Bounded::*;
63 match self {
64 Legacy { hash, .. } => Legacy { hash, dummy: core::marker::PhantomData },
65 Inline(x) => Inline(x),
66 Lookup { hash, len } => Lookup { hash, len },
67 }
68 }
69
70 pub fn hash(&self) -> H::Output {
74 use Bounded::*;
75 match self {
76 Lookup { hash, .. } | Legacy { hash, .. } => *hash,
77 Inline(x) => <H as Hash>::hash(x.as_ref()),
78 }
79 }
80
81 pub fn lookup_hash(&self) -> Option<H::Output> {
85 use Bounded::*;
86 match self {
87 Lookup { hash, .. } | Legacy { hash, .. } => Some(*hash),
88 Inline(_) => None,
89 }
90 }
91
92 pub fn len(&self) -> Option<u32> {
94 match self {
95 Self::Legacy { .. } => None,
96 Self::Inline(i) => Some(i.len() as u32),
97 Self::Lookup { len, .. } => Some(*len),
98 }
99 }
100
101 pub fn lookup_needed(&self) -> bool {
103 match self {
104 Self::Inline(..) => false,
105 Self::Legacy { .. } | Self::Lookup { .. } => true,
106 }
107 }
108
109 pub fn lookup_len(&self) -> Option<u32> {
111 match self {
112 Self::Inline(..) => None,
113 Self::Legacy { .. } => Some(MAX_LEGACY_LEN),
114 Self::Lookup { len, .. } => Some(*len),
115 }
116 }
117
118 pub fn unrequested(hash: H::Output, len: u32) -> Self {
120 Self::Lookup { hash, len }
121 }
122
123 #[deprecated = "This API is only for transitioning to Scheduler v3 API"]
125 pub fn from_legacy_hash(hash: impl Into<H::Output>) -> Self {
126 Self::Legacy { hash: hash.into(), dummy: core::marker::PhantomData }
127 }
128}
129
130pub type FetchResult = Result<Cow<'static, [u8]>, DispatchError>;
131
132pub trait QueryPreimage {
134 type H: Hash;
136
137 fn len(hash: &<Self::H as sp_core::Hasher>::Out) -> Option<u32>;
139
140 fn fetch(hash: &<Self::H as sp_core::Hasher>::Out, len: Option<u32>) -> FetchResult;
142
143 fn is_requested(hash: &<Self::H as sp_core::Hasher>::Out) -> bool;
145
146 fn request(hash: &<Self::H as sp_core::Hasher>::Out);
149
150 fn unrequest(hash: &<Self::H as sp_core::Hasher>::Out);
152
153 fn hold<T>(bounded: &Bounded<T, Self::H>) {
155 use Bounded::*;
156 match bounded {
157 Inline(..) => {},
158 Legacy { hash, .. } | Lookup { hash, .. } => Self::request(hash),
159 }
160 }
161
162 fn drop<T>(bounded: &Bounded<T, Self::H>) {
165 use Bounded::*;
166 match bounded {
167 Inline(..) => {},
168 Legacy { hash, .. } | Lookup { hash, .. } => Self::unrequest(hash),
169 }
170 }
171
172 fn have<T>(bounded: &Bounded<T, Self::H>) -> bool {
175 use Bounded::*;
176 match bounded {
177 Inline(..) => true,
178 Legacy { hash, .. } | Lookup { hash, .. } => Self::len(hash).is_some(),
179 }
180 }
181
182 fn pick<T>(hash: <Self::H as sp_core::Hasher>::Out, len: u32) -> Bounded<T, Self::H> {
188 Self::request(&hash);
189 Bounded::Lookup { hash, len }
190 }
191
192 fn peek<T: Decode>(bounded: &Bounded<T, Self::H>) -> Result<(T, Option<u32>), DispatchError> {
198 use Bounded::*;
199 match bounded {
200 Inline(data) => T::decode(&mut &data[..]).ok().map(|x| (x, None)),
201 Lookup { hash, len } => {
202 let data = Self::fetch(hash, Some(*len))?;
203 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
204 },
205 Legacy { hash, .. } => {
206 let data = Self::fetch(hash, None)?;
207 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
208 },
209 }
210 .ok_or(DispatchError::Corruption)
211 }
212
213 fn realize<T: Decode>(
217 bounded: &Bounded<T, Self::H>,
218 ) -> Result<(T, Option<u32>), DispatchError> {
219 let r = Self::peek(bounded)?;
220 Self::drop(bounded);
221 Ok(r)
222 }
223}
224
225pub trait StorePreimage: QueryPreimage {
231 const MAX_LENGTH: usize;
235
236 fn note(bytes: Cow<[u8]>) -> Result<<Self::H as sp_core::Hasher>::Out, DispatchError>;
240
241 fn unnote(hash: &<Self::H as sp_core::Hasher>::Out) {
244 Self::unrequest(hash)
245 }
246
247 fn bound<T: Encode>(t: T) -> Result<Bounded<T, Self::H>, DispatchError> {
254 let data = t.encode();
255 let len = data.len() as u32;
256 Ok(match BoundedInline::try_from(data) {
257 Ok(bounded) => Bounded::Inline(bounded),
258 Err(unbounded) => Bounded::Lookup { hash: Self::note(unbounded.into())?, len },
259 })
260 }
261}
262
263impl QueryPreimage for () {
264 type H = sp_runtime::traits::BlakeTwo256;
265
266 fn len(_: &sp_core::H256) -> Option<u32> {
267 None
268 }
269 fn fetch(_: &sp_core::H256, _: Option<u32>) -> FetchResult {
270 Err(DispatchError::Unavailable)
271 }
272 fn is_requested(_: &sp_core::H256) -> bool {
273 false
274 }
275 fn request(_: &sp_core::H256) {}
276 fn unrequest(_: &sp_core::H256) {}
277}
278
279impl StorePreimage for () {
280 const MAX_LENGTH: usize = 0;
281 fn note(_: Cow<[u8]>) -> Result<sp_core::H256, DispatchError> {
282 Err(DispatchError::Exhausted)
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use crate::BoundedVec;
290 use sp_runtime::{bounded_vec, traits::BlakeTwo256};
291
292 #[test]
293 fn bounded_size_is_correct() {
294 assert_eq!(<Bounded<Vec<u8>, BlakeTwo256> as MaxEncodedLen>::max_encoded_len(), 131);
295 }
296
297 #[test]
298 fn bounded_basic_works() {
299 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
300 let len = data.len() as u32;
301 let hash = BlakeTwo256::hash(&data).into();
302
303 {
305 let bound: Bounded<Vec<u8>, BlakeTwo256> = Bounded::Inline(data.clone());
306 assert_eq!(bound.hash(), hash);
307 assert_eq!(bound.len(), Some(len));
308 assert!(!bound.lookup_needed());
309 assert_eq!(bound.lookup_len(), None);
310 }
311 {
313 let bound: Bounded<Vec<u8>, BlakeTwo256> =
314 Bounded::Legacy { hash, dummy: Default::default() };
315 assert_eq!(bound.hash(), hash);
316 assert_eq!(bound.len(), None);
317 assert!(bound.lookup_needed());
318 assert_eq!(bound.lookup_len(), Some(1_000_000));
319 }
320 {
322 let bound: Bounded<Vec<u8>, BlakeTwo256> =
323 Bounded::Lookup { hash, len: data.len() as u32 };
324 assert_eq!(bound.hash(), hash);
325 assert_eq!(bound.len(), Some(len));
326 assert!(bound.lookup_needed());
327 assert_eq!(bound.lookup_len(), Some(len));
328 }
329 }
330
331 #[test]
332 fn bounded_transmuting_works() {
333 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
334
335 let x: Bounded<String, BlakeTwo256> = Bounded::Inline(data.clone());
337 let y: Bounded<&str, BlakeTwo256> = x.transmute();
338 assert_eq!(y, Bounded::Inline(data));
339 }
340}