frame_support/traits/
preimages.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Stuff for dealing with hashed preimages.
19
20use 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
31/// The maximum we expect a single legacy hash lookup to be.
32const 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	/// A hash with no preimage length. We do not support creation of this except
40	/// for transitioning from legacy state. In the future we will make this a pure
41	/// `Dummy` item storing only the final `dummy` field.
42	Legacy { hash: H::Output, dummy: core::marker::PhantomData<T> },
43	/// A bounded `Call`. Its encoding must be at most 128 bytes.
44	Inline(BoundedInline),
45	/// A hash of the call together with an upper limit for its size.`
46	Lookup { hash: H::Output, len: u32 },
47}
48
49impl<T, H: Hash> Bounded<T, H> {
50	/// Casts the wrapped type into something that encodes alike.
51	///
52	/// # Examples
53	/// ```
54	/// use frame_support::{traits::Bounded, sp_runtime::traits::BlakeTwo256};
55	///
56	/// // Transmute from `String` to `&str`.
57	/// let x: Bounded<String, BlakeTwo256> = Bounded::Inline(Default::default());
58	/// let _: Bounded<&str, BlakeTwo256> = x.transmute();
59	/// ```
60	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	/// Returns the hash of the preimage.
73	///
74	/// The hash is re-calculated every time if the preimage is inlined.
75	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	/// Returns the hash to lookup the preimage.
84	///
85	/// If this is a `Bounded::Inline`, `None` is returned as no lookup is required.
86	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	/// Returns the length of the preimage or `None` if the length is unknown.
95	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	/// Returns whether the image will require a lookup to be peeked.
104	pub fn lookup_needed(&self) -> bool {
105		match self {
106			Self::Inline(..) => false,
107			Self::Legacy { .. } | Self::Lookup { .. } => true,
108		}
109	}
110
111	/// The maximum length of the lookup that is needed to peek `Self`.
112	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	/// Constructs a `Lookup` bounded item.
121	pub fn unrequested(hash: H::Output, len: u32) -> Self {
122		Self::Lookup { hash, len }
123	}
124
125	/// Constructs a `Legacy` bounded item.
126	#[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
134/// A interface for looking up preimages from their hash on chain.
135pub trait QueryPreimage {
136	/// The hasher used in the runtime.
137	type H: Hash;
138
139	/// Returns whether a preimage exists for a given hash and if so its length.
140	fn len(hash: &<Self::H as sp_core::Hasher>::Out) -> Option<u32>;
141
142	/// Returns the preimage for a given hash. If given, `len` must be the size of the preimage.
143	fn fetch(hash: &<Self::H as sp_core::Hasher>::Out, len: Option<u32>) -> FetchResult;
144
145	/// Returns whether a preimage request exists for a given hash.
146	fn is_requested(hash: &<Self::H as sp_core::Hasher>::Out) -> bool;
147
148	/// Request that someone report a preimage. Providers use this to optimise the economics for
149	/// preimage reporting.
150	fn request(hash: &<Self::H as sp_core::Hasher>::Out);
151
152	/// Cancel a previous preimage request.
153	fn unrequest(hash: &<Self::H as sp_core::Hasher>::Out);
154
155	/// Request that the data required for decoding the given `bounded` value is made available.
156	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	/// No longer request that the data required for decoding the given `bounded` value is made
165	/// available.
166	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	/// Check to see if all data required for the given `bounded` value is available for its
175	/// decoding.
176	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	/// Create a `Bounded` instance based on the `hash` and `len` of the encoded value.
185	///
186	/// It also directly requests the given `hash` using [`Self::request`].
187	///
188	/// This may not be `peek`-able or `realize`-able.
189	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	/// Convert the given `bounded` instance back into its original instance, also returning the
195	/// exact size of its encoded form if it needed to be looked-up from a stored preimage).
196	///
197	/// NOTE: This does not remove any data needed for realization. If you will no longer use the
198	/// `bounded`, call `realize` instead or call `drop` afterwards.
199	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	/// Convert the given `bounded` value back into its original instance. If successful,
216	/// `drop` any data backing it. This will not break the realisability of independently
217	/// created instances of `Bounded` which happen to have identical data.
218	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
227/// A interface for managing preimages to hashes on chain.
228///
229/// Note that this API does not assume any underlying user is calling, and thus
230/// does not handle any preimage ownership or fees. Other system level logic that
231/// uses this API should implement that on their own side.
232pub trait StorePreimage: QueryPreimage {
233	/// The maximum length of preimage we can store.
234	///
235	/// This is the maximum length of the *encoded* value that can be passed to `bound`.
236	const MAX_LENGTH: usize;
237
238	/// Request and attempt to store the bytes of a preimage on chain.
239	///
240	/// May return `DispatchError::Exhausted` if the preimage is just too big.
241	fn note(bytes: Cow<[u8]>) -> Result<<Self::H as sp_core::Hasher>::Out, DispatchError>;
242
243	/// Attempt to clear a previously noted preimage. Exactly the same as `unrequest` but is
244	/// provided for symmetry.
245	fn unnote(hash: &<Self::H as sp_core::Hasher>::Out) {
246		Self::unrequest(hash)
247	}
248
249	/// Convert an otherwise unbounded or large value into a type ready for placing in storage.
250	///
251	/// The result is a type whose `MaxEncodedLen` is 131 bytes.
252	///
253	/// NOTE: Once this API is used, you should use either `drop` or `realize`.
254	/// The value is also noted using [`Self::note`].
255	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		// Inline works
306		{
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		// Legacy works
314		{
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		// Lookup works
323		{
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		// Transmute a `String` into a `&str`.
338		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}