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
//! # Preimage Pallet
19
//!
20
//! - [`Config`]
21
//! - [`Call`]
22
//!
23
//! ## Overview
24
//!
25
//! The Preimage pallet allows for the users and the runtime to store the preimage
26
//! of a hash on chain. This can be used by other pallets for storing and managing
27
//! large byte-blobs.
28

            
29
#![cfg_attr(not(feature = "std"), no_std)]
30

            
31
#[cfg(feature = "runtime-benchmarks")]
32
mod benchmarking;
33
pub mod migration;
34
#[cfg(test)]
35
mod mock;
36
#[cfg(test)]
37
mod tests;
38
pub mod weights;
39

            
40
extern crate alloc;
41

            
42
use alloc::{borrow::Cow, vec::Vec};
43
use sp_runtime::{
44
	traits::{BadOrigin, Hash, Saturating},
45
	Perbill,
46
};
47

            
48
use codec::{Decode, Encode, MaxEncodedLen};
49
use frame_support::{
50
	dispatch::Pays,
51
	ensure,
52
	pallet_prelude::Get,
53
	traits::{
54
		Consideration, Currency, Defensive, FetchResult, Footprint, PreimageProvider,
55
		PreimageRecipient, QueryPreimage, ReservableCurrency, StorePreimage,
56
	},
57
	BoundedSlice, BoundedVec,
58
};
59
use scale_info::TypeInfo;
60
pub use weights::WeightInfo;
61

            
62
use frame_support::pallet_prelude::*;
63
use frame_system::pallet_prelude::*;
64

            
65
pub use pallet::*;
66

            
67
/// A type to note whether a preimage is owned by a user or the system.
68
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
69
pub enum OldRequestStatus<AccountId, Balance> {
70
	/// The associated preimage has not yet been requested by the system. The given deposit (if
71
	/// some) is being held until either it becomes requested or the user retracts the preimage.
72
	Unrequested { deposit: (AccountId, Balance), len: u32 },
73
	/// There are a non-zero number of outstanding requests for this hash by this chain. If there
74
	/// is a preimage registered, then `len` is `Some` and it may be removed iff this counter
75
	/// becomes zero.
76
	Requested { deposit: Option<(AccountId, Balance)>, count: u32, len: Option<u32> },
77
}
78

            
79
/// A type to note whether a preimage is owned by a user or the system.
80
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
81
pub enum RequestStatus<AccountId, Ticket> {
82
558
	/// The associated preimage has not yet been requested by the system. The given deposit (if
83
	/// some) is being held until either it becomes requested or the user retracts the preimage.
84
558
	Unrequested { ticket: (AccountId, Ticket), len: u32 },
85
	/// There are a non-zero number of outstanding requests for this hash by this chain. If there
86
	/// is a preimage registered, then `len` is `Some` and it may be removed iff this counter
87
	/// becomes zero.
88
	Requested { maybe_ticket: Option<(AccountId, Ticket)>, count: u32, maybe_len: Option<u32> },
89
}
90

            
91
type BalanceOf<T> =
92
	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
93
type TicketOf<T> = <T as Config>::Consideration;
94

            
95
/// Maximum size of preimage we can store is 4mb.
96
const MAX_SIZE: u32 = 4 * 1024 * 1024;
97
/// Hard-limit on the number of hashes that can be passed to `ensure_updated`.
98
///
99
/// Exists only for benchmarking purposes.
100
pub const MAX_HASH_UPGRADE_BULK_COUNT: u32 = 1024;
101

            
102
1315
#[frame_support::pallet]
103
#[allow(deprecated)]
104
pub mod pallet {
105
	use super::*;
106

            
107
	/// The in-code storage version.
108
	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
109

            
110
	#[pallet::config]
111
	pub trait Config: frame_system::Config {
112
		/// The overarching event type.
113
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
114

            
115
		/// The Weight information for this pallet.
116
		type WeightInfo: weights::WeightInfo;
117

            
118
		/// Currency type for this pallet.
119
		// TODO#1569: Remove.
120
		type Currency: ReservableCurrency<Self::AccountId>;
121

            
122
		/// An origin that can request a preimage be placed on-chain without a deposit or fee, or
123
		/// manage existing preimages.
124
		type ManagerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
125

            
126
		/// A means of providing some cost while data is stored on-chain.
127
		///
128
		/// Should never return a `None`, implying no cost for a non-empty preimage.
129
		type Consideration: Consideration<Self::AccountId, Footprint>;
130
	}
131

            
132
554790
	#[pallet::pallet]
133
	#[pallet::storage_version(STORAGE_VERSION)]
134
	pub struct Pallet<T>(_);
135

            
136
	#[pallet::event]
137
471
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
138
	pub enum Event<T: Config> {
139
		/// A preimage has been noted.
140
		Noted { hash: T::Hash },
141
		/// A preimage has been requested.
142
		Requested { hash: T::Hash },
143
		/// A preimage has ben cleared.
144
		Cleared { hash: T::Hash },
145
	}
146

            
147
1290
	#[pallet::error]
148
	pub enum Error<T> {
149
		/// Preimage is too large to store on-chain.
150
		TooBig,
151
		/// Preimage has already been noted on-chain.
152
		AlreadyNoted,
153
		/// The user is not authorized to perform this action.
154
		NotAuthorized,
155
		/// The preimage cannot be removed since it has not yet been noted.
156
		NotNoted,
157
		/// A preimage may not be removed when there are outstanding requests.
158
		Requested,
159
		/// The preimage request cannot be removed since no outstanding requests exist.
160
		NotRequested,
161
		/// More than `MAX_HASH_UPGRADE_BULK_COUNT` hashes were requested to be upgraded at once.
162
		TooMany,
163
		/// Too few hashes were requested to be upgraded (i.e. zero).
164
		TooFew,
165
		/// No ticket with a cost was returned by [`Config::Consideration`] to store the preimage.
166
		NoCost,
167
	}
168

            
169
	/// A reason for this pallet placing a hold on funds.
170
	#[pallet::composite_enum]
171
	pub enum HoldReason {
172
1962
		/// The funds are held as storage deposit for a preimage.
173
1962
		Preimage,
174
	}
175

            
176
	/// The request status of a given hash.
177
	#[deprecated = "RequestStatusFor"]
178
2046
	#[pallet::storage]
179
	pub(super) type StatusFor<T: Config> =
180
		StorageMap<_, Identity, T::Hash, OldRequestStatus<T::AccountId, BalanceOf<T>>>;
181

            
182
	/// The request status of a given hash.
183
2007
	#[pallet::storage]
184
	pub(super) type RequestStatusFor<T: Config> =
185
		StorageMap<_, Identity, T::Hash, RequestStatus<T::AccountId, TicketOf<T>>>;
186

            
187
471
	#[pallet::storage]
188
	pub(super) type PreimageFor<T: Config> =
189
		StorageMap<_, Identity, (T::Hash, u32), BoundedVec<u8, ConstU32<MAX_SIZE>>>;
190

            
191
9090
	#[pallet::call(weight = T::WeightInfo)]
192
	impl<T: Config> Pallet<T> {
193
		/// Register a preimage on-chain.
194
		///
195
		/// If the preimage was previously requested, no fees or deposits are taken for providing
196
		/// the preimage. Otherwise, a deposit is taken proportional to the size of the preimage.
197
		#[pallet::call_index(0)]
198
		#[pallet::weight(T::WeightInfo::note_preimage(bytes.len() as u32))]
199
1068
		pub fn note_preimage(origin: OriginFor<T>, bytes: Vec<u8>) -> DispatchResultWithPostInfo {
200
			// We accept a signed origin which will pay a deposit, or a root origin where a deposit
201
			// is not taken.
202
1068
			let maybe_sender = Self::ensure_signed_or_manager(origin)?;
203
1068
			let (system_requested, _) = Self::note_bytes(bytes.into(), maybe_sender.as_ref())?;
204
471
			if system_requested || maybe_sender.is_none() {
205
				Ok(Pays::No.into())
206
			} else {
207
471
				Ok(().into())
208
			}
209
		}
210

            
211
		/// Clear an unrequested preimage from the runtime storage.
212
		///
213
		/// If `len` is provided, then it will be a much cheaper operation.
214
		///
215
		/// - `hash`: The hash of the preimage to be removed from the store.
216
		/// - `len`: The length of the preimage of `hash`.
217
		#[pallet::call_index(1)]
218
30
		pub fn unnote_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
219
30
			let maybe_sender = Self::ensure_signed_or_manager(origin)?;
220
30
			Self::do_unnote_preimage(&hash, maybe_sender)
221
		}
222

            
223
		/// Request a preimage be uploaded to the chain without paying any fees or deposits.
224
		///
225
		/// If the preimage requests has already been provided on-chain, we unreserve any deposit
226
		/// a user may have paid, and take the control of the preimage out of their hands.
227
		#[pallet::call_index(2)]
228
93
		pub fn request_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
229
93
			T::ManagerOrigin::ensure_origin(origin)?;
230
			Self::do_request_preimage(&hash);
231
			Ok(())
232
		}
233

            
234
		/// Clear a previously made request for a preimage.
235
		///
236
		/// NOTE: THIS MUST NOT BE CALLED ON `hash` MORE TIMES THAN `request_preimage`.
237
		#[pallet::call_index(3)]
238
30
		pub fn unrequest_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
239
30
			T::ManagerOrigin::ensure_origin(origin)?;
240
			Self::do_unrequest_preimage(&hash)
241
		}
242

            
243
		/// Ensure that the a bulk of pre-images is upgraded.
244
		///
245
		/// The caller pays no fee if at least 90% of pre-images were successfully updated.
246
		#[pallet::call_index(4)]
247
		#[pallet::weight(T::WeightInfo::ensure_updated(hashes.len() as u32))]
248
		pub fn ensure_updated(
249
			origin: OriginFor<T>,
250
			hashes: Vec<T::Hash>,
251
87
		) -> DispatchResultWithPostInfo {
252
87
			ensure_signed(origin)?;
253
87
			ensure!(hashes.len() > 0, Error::<T>::TooFew);
254
30
			ensure!(hashes.len() <= MAX_HASH_UPGRADE_BULK_COUNT as usize, Error::<T>::TooMany);
255

            
256
520
			let updated = hashes.iter().map(Self::do_ensure_updated).filter(|b| *b).count() as u32;
257
30
			let ratio = Perbill::from_rational(updated, hashes.len() as u32);
258
30

            
259
30
			let pays: Pays = (ratio < Perbill::from_percent(90)).into();
260
30
			Ok(pays.into())
261
		}
262
	}
263
}
264

            
265
impl<T: Config> Pallet<T> {
266
2046
	fn do_ensure_updated(h: &T::Hash) -> bool {
267
		#[allow(deprecated)]
268
2046
		let r = match StatusFor::<T>::take(h) {
269
			Some(r) => r,
270
2046
			None => return false,
271
		};
272
		let n = match r {
273
			OldRequestStatus::Unrequested { deposit: (who, amount), len } => {
274
				// unreserve deposit
275
				T::Currency::unreserve(&who, amount);
276
				// take consideration
277
				let Ok(Some(ticket)) =
278
					T::Consideration::new(&who, Footprint::from_parts(1, len as usize))
279
				else {
280
					defensive!("None ticket or inability to take deposit after unreserved");
281
					return true
282
				};
283
				RequestStatus::Unrequested { ticket: (who, ticket), len }
284
			},
285
			OldRequestStatus::Requested { deposit: maybe_deposit, count, len: maybe_len } => {
286
				let maybe_ticket = if let Some((who, deposit)) = maybe_deposit {
287
					// unreserve deposit
288
					T::Currency::unreserve(&who, deposit);
289
					// take consideration
290
					if let Some(len) = maybe_len {
291
						let Ok(Some(ticket)) =
292
							T::Consideration::new(&who, Footprint::from_parts(1, len as usize))
293
						else {
294
							defensive!("None ticket or inability to take deposit after unreserved");
295
							return true
296
						};
297
						Some((who, ticket))
298
					} else {
299
						None
300
					}
301
				} else {
302
					None
303
				};
304
				RequestStatus::Requested { maybe_ticket, count, maybe_len }
305
			},
306
		};
307
		RequestStatusFor::<T>::insert(h, n);
308
		true
309
2046
	}
310

            
311
	/// Ensure that the origin is either the `ManagerOrigin` or a signed origin.
312
1098
	fn ensure_signed_or_manager(
313
1098
		origin: T::RuntimeOrigin,
314
1098
	) -> Result<Option<T::AccountId>, BadOrigin> {
315
1098
		if T::ManagerOrigin::ensure_origin(origin.clone()).is_ok() {
316
			return Ok(None)
317
1098
		}
318
1098
		let who = ensure_signed(origin)?;
319
1098
		Ok(Some(who))
320
1098
	}
321

            
322
	/// Store some preimage on chain.
323
	///
324
	/// If `maybe_depositor` is `None` then it is also requested. If `Some`, then it is not.
325
	///
326
	/// We verify that the preimage is within the bounds of what the pallet supports.
327
	///
328
	/// If the preimage was requested to be uploaded, then the user pays no deposits or tx fees.
329
1068
	fn note_bytes(
330
1068
		preimage: Cow<[u8]>,
331
1068
		maybe_depositor: Option<&T::AccountId>,
332
1068
	) -> Result<(bool, T::Hash), DispatchError> {
333
1068
		let hash = T::Hashing::hash(&preimage);
334
1068
		let len = preimage.len() as u32;
335
1068
		ensure!(len <= MAX_SIZE, Error::<T>::TooBig);
336

            
337
1068
		Self::do_ensure_updated(&hash);
338
		// We take a deposit only if there is a provided depositor and the preimage was not
339
		// previously requested. This also allows the tx to pay no fee.
340
1068
		let status = match (RequestStatusFor::<T>::get(hash), maybe_depositor) {
341
			(Some(RequestStatus::Requested { maybe_ticket, count, .. }), _) =>
342
				RequestStatus::Requested { maybe_ticket, count, maybe_len: Some(len) },
343
			(Some(RequestStatus::Unrequested { .. }), Some(_)) =>
344
558
				return Err(Error::<T>::AlreadyNoted.into()),
345
			(Some(RequestStatus::Unrequested { ticket, len }), None) => RequestStatus::Requested {
346
				maybe_ticket: Some(ticket),
347
				count: 1,
348
				maybe_len: Some(len),
349
			},
350
			(None, None) =>
351
				RequestStatus::Requested { maybe_ticket: None, count: 1, maybe_len: Some(len) },
352
510
			(None, Some(depositor)) => {
353
471
				let ticket =
354
510
					T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))?
355
471
						.ok_or(Error::<T>::NoCost)?;
356
471
				RequestStatus::Unrequested { ticket: (depositor.clone(), ticket), len }
357
			},
358
		};
359
471
		let was_requested = matches!(status, RequestStatus::Requested { .. });
360
471
		RequestStatusFor::<T>::insert(hash, status);
361
471

            
362
471
		let _ = Self::insert(&hash, preimage)
363
471
			.defensive_proof("Unable to insert. Logic error in `note_bytes`?");
364
471

            
365
471
		Self::deposit_event(Event::Noted { hash });
366
471

            
367
471
		Ok((was_requested, hash))
368
1068
	}
369

            
370
	// This function will add a hash to the list of requested preimages.
371
	//
372
	// If the preimage already exists before the request is made, the deposit for the preimage is
373
	// returned to the user, and removed from their management.
374
	fn do_request_preimage(hash: &T::Hash) {
375
		Self::do_ensure_updated(&hash);
376
		let (count, maybe_len, maybe_ticket) =
377
			RequestStatusFor::<T>::get(hash).map_or((1, None, None), |x| match x {
378
				RequestStatus::Requested { maybe_ticket, mut count, maybe_len } => {
379
					count.saturating_inc();
380
					(count, maybe_len, maybe_ticket)
381
				},
382
				RequestStatus::Unrequested { ticket, len } => (1, Some(len), Some(ticket)),
383
			});
384
		RequestStatusFor::<T>::insert(
385
			hash,
386
			RequestStatus::Requested { maybe_ticket, count, maybe_len },
387
		);
388
		if count == 1 {
389
			Self::deposit_event(Event::Requested { hash: *hash });
390
		}
391
	}
392

            
393
	// Clear a preimage from the storage of the chain, returning any deposit that may be reserved.
394
	//
395
	// If `len` is provided, it will be a much cheaper operation.
396
	//
397
	// If `maybe_owner` is provided, we verify that it is the correct owner before clearing the
398
	// data.
399
30
	fn do_unnote_preimage(
400
30
		hash: &T::Hash,
401
30
		maybe_check_owner: Option<T::AccountId>,
402
30
	) -> DispatchResult {
403
30
		Self::do_ensure_updated(&hash);
404
30
		match RequestStatusFor::<T>::get(hash).ok_or(Error::<T>::NotNoted)? {
405
			RequestStatus::Requested { maybe_ticket: Some((owner, ticket)), count, maybe_len } => {
406
				ensure!(maybe_check_owner.map_or(true, |c| c == owner), Error::<T>::NotAuthorized);
407
				let _ = ticket.drop(&owner);
408
				RequestStatusFor::<T>::insert(
409
					hash,
410
					RequestStatus::Requested { maybe_ticket: None, count, maybe_len },
411
				);
412
				Ok(())
413
			},
414
			RequestStatus::Requested { maybe_ticket: None, .. } => {
415
				ensure!(maybe_check_owner.is_none(), Error::<T>::NotAuthorized);
416
				Self::do_unrequest_preimage(hash)
417
			},
418
			RequestStatus::Unrequested { ticket: (owner, ticket), len } => {
419
				ensure!(maybe_check_owner.map_or(true, |c| c == owner), Error::<T>::NotAuthorized);
420
				let _ = ticket.drop(&owner);
421
				RequestStatusFor::<T>::remove(hash);
422

            
423
				Self::remove(hash, len);
424
				Self::deposit_event(Event::Cleared { hash: *hash });
425
				Ok(())
426
			},
427
		}
428
30
	}
429

            
430
	/// Clear a preimage request.
431
	fn do_unrequest_preimage(hash: &T::Hash) -> DispatchResult {
432
		Self::do_ensure_updated(&hash);
433
		match RequestStatusFor::<T>::get(hash).ok_or(Error::<T>::NotRequested)? {
434
			RequestStatus::Requested { mut count, maybe_len, maybe_ticket } if count > 1 => {
435
				count.saturating_dec();
436
				RequestStatusFor::<T>::insert(
437
					hash,
438
					RequestStatus::Requested { maybe_ticket, count, maybe_len },
439
				);
440
			},
441
			RequestStatus::Requested { count, maybe_len, maybe_ticket } => {
442
				debug_assert!(count == 1, "preimage request counter at zero?");
443
				match (maybe_len, maybe_ticket) {
444
					// Preimage was never noted.
445
					(None, _) => RequestStatusFor::<T>::remove(hash),
446
					// Preimage was noted without owner - just remove it.
447
					(Some(len), None) => {
448
						Self::remove(hash, len);
449
						RequestStatusFor::<T>::remove(hash);
450
						Self::deposit_event(Event::Cleared { hash: *hash });
451
					},
452
					// Preimage was noted with owner - move to unrequested so they can get refund.
453
					(Some(len), Some(ticket)) => {
454
						RequestStatusFor::<T>::insert(
455
							hash,
456
							RequestStatus::Unrequested { ticket, len },
457
						);
458
					},
459
				}
460
			},
461
			RequestStatus::Unrequested { .. } => return Err(Error::<T>::NotRequested.into()),
462
		}
463
		Ok(())
464
	}
465

            
466
471
	fn insert(hash: &T::Hash, preimage: Cow<[u8]>) -> Result<(), ()> {
467
471
		BoundedSlice::<u8, ConstU32<MAX_SIZE>>::try_from(preimage.as_ref())
468
471
			.map_err(|_| ())
469
471
			.map(|s| PreimageFor::<T>::insert((hash, s.len() as u32), s))
470
471
	}
471

            
472
	fn remove(hash: &T::Hash, len: u32) {
473
		PreimageFor::<T>::remove((hash, len))
474
	}
475

            
476
	fn have(hash: &T::Hash) -> bool {
477
		Self::len(hash).is_some()
478
	}
479

            
480
438
	fn len(hash: &T::Hash) -> Option<u32> {
481
		use RequestStatus::*;
482
438
		Self::do_ensure_updated(&hash);
483
438
		match RequestStatusFor::<T>::get(hash) {
484
			Some(Requested { maybe_len: Some(len), .. }) | Some(Unrequested { len, .. }) =>
485
				Some(len),
486
438
			_ => None,
487
		}
488
438
	}
489

            
490
	fn fetch(hash: &T::Hash, len: Option<u32>) -> FetchResult {
491
		let len = len.or_else(|| Self::len(hash)).ok_or(DispatchError::Unavailable)?;
492
		PreimageFor::<T>::get((hash, len))
493
			.map(|p| p.into_inner())
494
			.map(Into::into)
495
			.ok_or(DispatchError::Unavailable)
496
	}
497
}
498

            
499
impl<T: Config> PreimageProvider<T::Hash> for Pallet<T> {
500
	fn have_preimage(hash: &T::Hash) -> bool {
501
		Self::have(hash)
502
	}
503

            
504
	fn preimage_requested(hash: &T::Hash) -> bool {
505
		Self::do_ensure_updated(hash);
506
		matches!(RequestStatusFor::<T>::get(hash), Some(RequestStatus::Requested { .. }))
507
	}
508

            
509
	fn get_preimage(hash: &T::Hash) -> Option<Vec<u8>> {
510
		Self::fetch(hash, None).ok().map(Cow::into_owned)
511
	}
512

            
513
	fn request_preimage(hash: &T::Hash) {
514
		Self::do_request_preimage(hash)
515
	}
516

            
517
	fn unrequest_preimage(hash: &T::Hash) {
518
		let res = Self::do_unrequest_preimage(hash);
519
		debug_assert!(res.is_ok(), "do_unrequest_preimage failed - counter underflow?");
520
	}
521
}
522

            
523
impl<T: Config> PreimageRecipient<T::Hash> for Pallet<T> {
524
	type MaxSize = ConstU32<MAX_SIZE>; // 2**22
525

            
526
	fn note_preimage(bytes: BoundedVec<u8, Self::MaxSize>) {
527
		// We don't really care if this fails, since that's only the case if someone else has
528
		// already noted it.
529
		let _ = Self::note_bytes(bytes.into_inner().into(), None);
530
	}
531

            
532
	fn unnote_preimage(hash: &T::Hash) {
533
		// Should never fail if authorization check is skipped.
534
		let res = Self::do_unrequest_preimage(hash);
535
		debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?");
536
	}
537
}
538

            
539
impl<T: Config> QueryPreimage for Pallet<T> {
540
	type H = T::Hashing;
541

            
542
438
	fn len(hash: &T::Hash) -> Option<u32> {
543
438
		Pallet::<T>::len(hash)
544
438
	}
545

            
546
	fn fetch(hash: &T::Hash, len: Option<u32>) -> FetchResult {
547
		Pallet::<T>::fetch(hash, len)
548
	}
549

            
550
	fn is_requested(hash: &T::Hash) -> bool {
551
		Self::do_ensure_updated(&hash);
552
		matches!(RequestStatusFor::<T>::get(hash), Some(RequestStatus::Requested { .. }))
553
	}
554

            
555
	fn request(hash: &T::Hash) {
556
		Self::do_request_preimage(hash)
557
	}
558

            
559
	fn unrequest(hash: &T::Hash) {
560
		let res = Self::do_unrequest_preimage(hash);
561
		debug_assert!(res.is_ok(), "do_unrequest_preimage failed - counter underflow?");
562
	}
563
}
564

            
565
impl<T: Config> StorePreimage for Pallet<T> {
566
	const MAX_LENGTH: usize = MAX_SIZE as usize;
567

            
568
	fn note(bytes: Cow<[u8]>) -> Result<T::Hash, DispatchError> {
569
		// We don't really care if this fails, since that's only the case if someone else has
570
		// already noted it.
571
		let maybe_hash = Self::note_bytes(bytes, None).map(|(_, h)| h);
572
		// Map to the correct trait error.
573
		if maybe_hash == Err(DispatchError::from(Error::<T>::TooBig)) {
574
			Err(DispatchError::Exhausted)
575
		} else {
576
			maybe_hash
577
		}
578
	}
579

            
580
	fn unnote(hash: &T::Hash) {
581
		// Should never fail if authorization check is skipped.
582
		let res = Self::do_unnote_preimage(hash, None);
583
		debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?");
584
	}
585
}