1use alloc::borrow::Cow;
10use codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen};
11use scale_info::TypeInfo;
12use subsoil::runtime::{
13 traits::{ConstU32, Hash},
14 DispatchError,
15};
16use Debug;
17
18pub type BoundedInline = crate::BoundedVec<u8, ConstU32<128>>;
19
20const MAX_LEGACY_LEN: u32 = 1_000_000;
22
23#[derive(
24 Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, Clone, Eq, PartialEq, TypeInfo, Debug,
25)]
26#[codec(mel_bound())]
27pub enum Bounded<T, H: Hash> {
28 Legacy { hash: H::Output, dummy: core::marker::PhantomData<T> },
32 Inline(BoundedInline),
34 Lookup { hash: H::Output, len: u32 },
36}
37
38impl<T, H: Hash> Bounded<T, H> {
39 pub fn transmute<S: Encode>(self) -> Bounded<S, H>
50 where
51 T: Encode + EncodeLike<S>,
52 {
53 use Bounded::*;
54 match self {
55 Legacy { hash, .. } => Legacy { hash, dummy: core::marker::PhantomData },
56 Inline(x) => Inline(x),
57 Lookup { hash, len } => Lookup { hash, len },
58 }
59 }
60
61 pub fn hash(&self) -> H::Output {
65 use Bounded::*;
66 match self {
67 Lookup { hash, .. } | Legacy { hash, .. } => *hash,
68 Inline(x) => <H as Hash>::hash(x.as_ref()),
69 }
70 }
71
72 pub fn lookup_hash(&self) -> Option<H::Output> {
76 use Bounded::*;
77 match self {
78 Lookup { hash, .. } | Legacy { hash, .. } => Some(*hash),
79 Inline(_) => None,
80 }
81 }
82
83 pub fn len(&self) -> Option<u32> {
85 match self {
86 Self::Legacy { .. } => None,
87 Self::Inline(i) => Some(i.len() as u32),
88 Self::Lookup { len, .. } => Some(*len),
89 }
90 }
91
92 pub fn lookup_needed(&self) -> bool {
94 match self {
95 Self::Inline(..) => false,
96 Self::Legacy { .. } | Self::Lookup { .. } => true,
97 }
98 }
99
100 pub fn lookup_len(&self) -> Option<u32> {
102 match self {
103 Self::Inline(..) => None,
104 Self::Legacy { .. } => Some(MAX_LEGACY_LEN),
105 Self::Lookup { len, .. } => Some(*len),
106 }
107 }
108
109 pub fn unrequested(hash: H::Output, len: u32) -> Self {
111 Self::Lookup { hash, len }
112 }
113
114 #[deprecated = "This API is only for transitioning to Scheduler v3 API"]
116 pub fn from_legacy_hash(hash: impl Into<H::Output>) -> Self {
117 Self::Legacy { hash: hash.into(), dummy: core::marker::PhantomData }
118 }
119}
120
121pub type FetchResult = Result<Cow<'static, [u8]>, DispatchError>;
122
123pub trait QueryPreimage {
125 type H: Hash;
127
128 fn len(hash: &<Self::H as subsoil::core::Hasher>::Out) -> Option<u32>;
130
131 fn fetch(hash: &<Self::H as subsoil::core::Hasher>::Out, len: Option<u32>) -> FetchResult;
133
134 fn is_requested(hash: &<Self::H as subsoil::core::Hasher>::Out) -> bool;
136
137 fn request(hash: &<Self::H as subsoil::core::Hasher>::Out);
140
141 fn unrequest(hash: &<Self::H as subsoil::core::Hasher>::Out);
143
144 fn hold<T>(bounded: &Bounded<T, Self::H>) {
146 use Bounded::*;
147 match bounded {
148 Inline(..) => {},
149 Legacy { hash, .. } | Lookup { hash, .. } => Self::request(hash),
150 }
151 }
152
153 fn drop<T>(bounded: &Bounded<T, Self::H>) {
156 use Bounded::*;
157 match bounded {
158 Inline(..) => {},
159 Legacy { hash, .. } | Lookup { hash, .. } => Self::unrequest(hash),
160 }
161 }
162
163 fn have<T>(bounded: &Bounded<T, Self::H>) -> bool {
166 use Bounded::*;
167 match bounded {
168 Inline(..) => true,
169 Legacy { hash, .. } | Lookup { hash, .. } => Self::len(hash).is_some(),
170 }
171 }
172
173 fn pick<T>(hash: <Self::H as subsoil::core::Hasher>::Out, len: u32) -> Bounded<T, Self::H> {
179 Self::request(&hash);
180 Bounded::Lookup { hash, len }
181 }
182
183 fn peek<T: Decode>(bounded: &Bounded<T, Self::H>) -> Result<(T, Option<u32>), DispatchError> {
189 use Bounded::*;
190 match bounded {
191 Inline(data) => T::decode(&mut &data[..]).ok().map(|x| (x, None)),
192 Lookup { hash, len } => {
193 let data = Self::fetch(hash, Some(*len))?;
194 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
195 },
196 Legacy { hash, .. } => {
197 let data = Self::fetch(hash, None)?;
198 T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32)))
199 },
200 }
201 .ok_or(DispatchError::Corruption)
202 }
203
204 fn realize<T: Decode>(
208 bounded: &Bounded<T, Self::H>,
209 ) -> Result<(T, Option<u32>), DispatchError> {
210 let r = Self::peek(bounded)?;
211 Self::drop(bounded);
212 Ok(r)
213 }
214}
215
216pub trait StorePreimage: QueryPreimage {
222 const MAX_LENGTH: usize;
226
227 fn note(bytes: Cow<[u8]>) -> Result<<Self::H as subsoil::core::Hasher>::Out, DispatchError>;
231
232 fn unnote(hash: &<Self::H as subsoil::core::Hasher>::Out) {
235 Self::unrequest(hash)
236 }
237
238 fn bound<T: Encode>(t: T) -> Result<Bounded<T, Self::H>, DispatchError> {
245 let data = t.encode();
246 let len = data.len() as u32;
247 Ok(match BoundedInline::try_from(data) {
248 Ok(bounded) => Bounded::Inline(bounded),
249 Err(unbounded) => Bounded::Lookup { hash: Self::note(unbounded.into())?, len },
250 })
251 }
252}
253
254impl QueryPreimage for () {
255 type H = subsoil::runtime::traits::BlakeTwo256;
256
257 fn len(_: &subsoil::core::H256) -> Option<u32> {
258 None
259 }
260 fn fetch(_: &subsoil::core::H256, _: Option<u32>) -> FetchResult {
261 Err(DispatchError::Unavailable)
262 }
263 fn is_requested(_: &subsoil::core::H256) -> bool {
264 false
265 }
266 fn request(_: &subsoil::core::H256) {}
267 fn unrequest(_: &subsoil::core::H256) {}
268}
269
270impl StorePreimage for () {
271 const MAX_LENGTH: usize = 0;
272 fn note(_: Cow<[u8]>) -> Result<subsoil::core::H256, DispatchError> {
273 Err(DispatchError::Exhausted)
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use crate::BoundedVec;
281 use subsoil::runtime::{bounded_vec, traits::BlakeTwo256};
282
283 #[test]
284 fn bounded_size_is_correct() {
285 assert_eq!(<Bounded<Vec<u8>, BlakeTwo256> as MaxEncodedLen>::max_encoded_len(), 131);
286 }
287
288 #[test]
289 fn bounded_basic_works() {
290 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
291 let len = data.len() as u32;
292 let hash = BlakeTwo256::hash(&data).into();
293
294 {
296 let bound: Bounded<Vec<u8>, BlakeTwo256> = Bounded::Inline(data.clone());
297 assert_eq!(bound.hash(), hash);
298 assert_eq!(bound.len(), Some(len));
299 assert!(!bound.lookup_needed());
300 assert_eq!(bound.lookup_len(), None);
301 }
302 {
304 let bound: Bounded<Vec<u8>, BlakeTwo256> =
305 Bounded::Legacy { hash, dummy: Default::default() };
306 assert_eq!(bound.hash(), hash);
307 assert_eq!(bound.len(), None);
308 assert!(bound.lookup_needed());
309 assert_eq!(bound.lookup_len(), Some(1_000_000));
310 }
311 {
313 let bound: Bounded<Vec<u8>, BlakeTwo256> =
314 Bounded::Lookup { hash, len: data.len() as u32 };
315 assert_eq!(bound.hash(), hash);
316 assert_eq!(bound.len(), Some(len));
317 assert!(bound.lookup_needed());
318 assert_eq!(bound.lookup_len(), Some(len));
319 }
320 }
321
322 #[test]
323 fn bounded_transmuting_works() {
324 let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c'];
325
326 let x: Bounded<String, BlakeTwo256> = Bounded::Inline(data.clone());
328 let y: Bounded<&str, BlakeTwo256> = x.transmute();
329 assert_eq!(y, Bounded::Inline(data));
330 }
331}