pezsp_runtime/generic/
unchecked_extrinsic.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
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//! Generic implementation of an unchecked (pre-verification) extrinsic.
19
20use crate::{
21	generic::{CheckedExtrinsic, ExtrinsicFormat},
22	traits::{
23		self, transaction_extension::TransactionExtension, Checkable, Dispatchable, ExtrinsicCall,
24		ExtrinsicLike, ExtrinsicMetadata, IdentifyAccount, LazyExtrinsic, MaybeDisplay, Member,
25		SignaturePayload,
26	},
27	transaction_validity::{InvalidTransaction, TransactionValidityError},
28	OpaqueExtrinsic,
29};
30#[cfg(all(not(feature = "std"), feature = "serde"))]
31use alloc::format;
32use alloc::{vec, vec::Vec};
33use codec::{
34	Compact, CountedInput, Decode, DecodeWithMemLimit, DecodeWithMemTracking, Encode, EncodeLike,
35	Input,
36};
37use core::fmt;
38use pezsp_io::hashing::blake2_256;
39use pezsp_weights::Weight;
40use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter};
41
42/// Type to represent the version of the [Extension](TransactionExtension) used in this extrinsic.
43pub type ExtensionVersion = u8;
44/// Type to represent the extrinsic format version which defines an [UncheckedExtrinsic].
45pub type ExtrinsicVersion = u8;
46
47/// Current version of the [`UncheckedExtrinsic`] encoded format.
48///
49/// This version needs to be bumped if the encoded representation changes.
50/// It ensures that if the representation is changed and the format is not known,
51/// the decoding fails.
52pub const EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 5;
53/// Legacy version of the [`UncheckedExtrinsic`] encoded format.
54///
55/// This version was used in the signed/unsigned transaction model and is still supported for
56/// compatibility reasons. It will be deprecated in favor of v5 extrinsics and an inherent/general
57/// transaction model.
58pub const LEGACY_EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 4;
59/// Current version of the [Extension](TransactionExtension) used in this
60/// [extrinsic](UncheckedExtrinsic).
61///
62/// This version needs to be bumped if there are breaking changes to the extension used in the
63/// [UncheckedExtrinsic] implementation.
64const EXTENSION_VERSION: ExtensionVersion = 0;
65
66/// Maximum decoded heap size for a runtime call (in bytes).
67pub const DEFAULT_MAX_CALL_SIZE: usize = 16 * 1024 * 1024; // 16 MiB
68
69/// The `SignaturePayload` of `UncheckedExtrinsic`.
70pub type UncheckedSignaturePayload<Address, Signature, Extension> = (Address, Signature, Extension);
71
72impl<Address: TypeInfo, Signature: TypeInfo, Extension: TypeInfo> SignaturePayload
73	for UncheckedSignaturePayload<Address, Signature, Extension>
74{
75	type SignatureAddress = Address;
76	type Signature = Signature;
77	type SignatureExtra = Extension;
78}
79
80/// A "header" for extrinsics leading up to the call itself. Determines the type of extrinsic and
81/// holds any necessary specialized data.
82#[derive(DecodeWithMemTracking, Eq, PartialEq, Clone)]
83pub enum Preamble<Address, Signature, Extension> {
84	/// An extrinsic without a signature or any extension. This means it's either an inherent or
85	/// an old-school "Unsigned" (we don't use that terminology any more since it's confusable with
86	/// the general transaction which is without a signature but does have an extension).
87	///
88	/// NOTE: In the future, once we remove `ValidateUnsigned`, this will only serve Inherent
89	/// extrinsics and thus can be renamed to `Inherent`.
90	Bare(ExtrinsicVersion),
91	/// An old-school transaction extrinsic which includes a signature of some hard-coded crypto.
92	/// Available only on extrinsic version 4.
93	Signed(Address, Signature, Extension),
94	/// A new-school transaction extrinsic which does not include a signature by default. The
95	/// origin authorization, through signatures or other means, is performed by the transaction
96	/// extension in this extrinsic. Available starting with extrinsic version 5.
97	General(ExtensionVersion, Extension),
98}
99
100const VERSION_MASK: u8 = 0b0011_1111;
101const TYPE_MASK: u8 = 0b1100_0000;
102const BARE_EXTRINSIC: u8 = 0b0000_0000;
103const SIGNED_EXTRINSIC: u8 = 0b1000_0000;
104const GENERAL_EXTRINSIC: u8 = 0b0100_0000;
105
106impl<Address, Signature, Extension> Decode for Preamble<Address, Signature, Extension>
107where
108	Address: Decode,
109	Signature: Decode,
110	Extension: Decode,
111{
112	fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
113		let version_and_type = input.read_byte()?;
114
115		let version = version_and_type & VERSION_MASK;
116		let xt_type = version_and_type & TYPE_MASK;
117
118		let preamble = match (version, xt_type) {
119			(
120				extrinsic_version @ LEGACY_EXTRINSIC_FORMAT_VERSION..=EXTRINSIC_FORMAT_VERSION,
121				BARE_EXTRINSIC,
122			) => Self::Bare(extrinsic_version),
123			(LEGACY_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC) => {
124				let address = Address::decode(input)?;
125				let signature = Signature::decode(input)?;
126				let ext = Extension::decode(input)?;
127				Self::Signed(address, signature, ext)
128			},
129			(EXTRINSIC_FORMAT_VERSION, GENERAL_EXTRINSIC) => {
130				let ext_version = ExtensionVersion::decode(input)?;
131				let ext = Extension::decode(input)?;
132				Self::General(ext_version, ext)
133			},
134			(_, _) => return Err("Invalid transaction version".into()),
135		};
136
137		Ok(preamble)
138	}
139}
140
141impl<Address, Signature, Extension> Encode for Preamble<Address, Signature, Extension>
142where
143	Address: Encode,
144	Signature: Encode,
145	Extension: Encode,
146{
147	fn size_hint(&self) -> usize {
148		match &self {
149			Preamble::Bare(_) => EXTRINSIC_FORMAT_VERSION.size_hint(),
150			Preamble::Signed(address, signature, ext) => LEGACY_EXTRINSIC_FORMAT_VERSION
151				.size_hint()
152				.saturating_add(address.size_hint())
153				.saturating_add(signature.size_hint())
154				.saturating_add(ext.size_hint()),
155			Preamble::General(ext_version, ext) => EXTRINSIC_FORMAT_VERSION
156				.size_hint()
157				.saturating_add(ext_version.size_hint())
158				.saturating_add(ext.size_hint()),
159		}
160	}
161
162	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
163		match &self {
164			Preamble::Bare(extrinsic_version) => {
165				(extrinsic_version | BARE_EXTRINSIC).encode_to(dest);
166			},
167			Preamble::Signed(address, signature, ext) => {
168				(LEGACY_EXTRINSIC_FORMAT_VERSION | SIGNED_EXTRINSIC).encode_to(dest);
169				address.encode_to(dest);
170				signature.encode_to(dest);
171				ext.encode_to(dest);
172			},
173			Preamble::General(ext_version, ext) => {
174				(EXTRINSIC_FORMAT_VERSION | GENERAL_EXTRINSIC).encode_to(dest);
175				ext_version.encode_to(dest);
176				ext.encode_to(dest);
177			},
178		}
179	}
180}
181
182impl<Address, Signature, Extension> Preamble<Address, Signature, Extension> {
183	/// Returns `Some` if this is a signed extrinsic, together with the relevant inner fields.
184	pub fn to_signed(self) -> Option<(Address, Signature, Extension)> {
185		match self {
186			Self::Signed(a, s, e) => Some((a, s, e)),
187			_ => None,
188		}
189	}
190}
191
192impl<Address, Signature, Extension> fmt::Debug for Preamble<Address, Signature, Extension>
193where
194	Address: fmt::Debug,
195	Extension: fmt::Debug,
196{
197	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198		match self {
199			Self::Bare(_) => write!(f, "Bare"),
200			Self::Signed(address, _, tx_ext) => write!(f, "Signed({:?}, {:?})", address, tx_ext),
201			Self::General(ext_version, tx_ext) => {
202				write!(f, "General({:?}, {:?})", ext_version, tx_ext)
203			},
204		}
205	}
206}
207
208/// An extrinsic right from the external world. This is unchecked and so can contain a signature.
209///
210/// An extrinsic is formally described as any external data that is originating from the outside of
211/// the runtime and fed into the runtime as a part of the block-body.
212///
213/// Inherents are special types of extrinsics that are placed into the block by the block-builder.
214/// They are unsigned because the assertion is that they are "inherently true" by virtue of getting
215/// past all validators.
216///
217/// Transactions are all other statements provided by external entities that the chain deems values
218/// and decided to include in the block. This value is typically in the form of fee payment, but it
219/// could in principle be any other interaction. Transactions are either signed or unsigned. A
220/// sensible transaction pool should ensure that only transactions that are worthwhile are
221/// considered for block-building.
222#[cfg_attr(all(feature = "std", not(windows)), doc = simple_mermaid::mermaid!("../../docs/mermaid/extrinsics.mmd"))]
223/// This type is by no means enforced within Bizinikiwi, but given its genericness, it is highly
224/// likely that for most use-cases it will suffice. Thus, the encoding of this type will dictate
225/// exactly what bytes should be sent to a runtime to transact with it.
226///
227/// This can be checked using [`Checkable`], yielding a [`CheckedExtrinsic`], which is the
228/// counterpart of this type after its signature (and other non-negotiable validity checks) have
229/// passed.
230#[derive(DecodeWithMemTracking, PartialEq, Eq, Clone, Debug)]
231#[codec(decode_with_mem_tracking_bound(
232	Address: DecodeWithMemTracking,
233	Call: DecodeWithMemTracking,
234	Signature: DecodeWithMemTracking,
235	Extension: DecodeWithMemTracking)
236)]
237pub struct UncheckedExtrinsic<
238	Address,
239	Call,
240	Signature,
241	Extension,
242	const MAX_CALL_SIZE: usize = DEFAULT_MAX_CALL_SIZE,
243> {
244	/// Information regarding the type of extrinsic this is (inherent or transaction) as well as
245	/// associated extension (`Extension`) data if it's a transaction and a possible signature.
246	pub preamble: Preamble<Address, Signature, Extension>,
247	/// The function that should be called.
248	pub function: Call,
249}
250
251/// Manual [`TypeInfo`] implementation because of custom encoding. The data is a valid encoded
252/// `Vec<u8>`, but requires some logic to extract the signature and payload.
253///
254/// See [`UncheckedExtrinsic::encode`] and [`UncheckedExtrinsic::decode`].
255impl<Address, Call, Signature, Extension> TypeInfo
256	for UncheckedExtrinsic<Address, Call, Signature, Extension>
257where
258	Address: StaticTypeInfo,
259	Call: StaticTypeInfo,
260	Signature: StaticTypeInfo,
261	Extension: StaticTypeInfo,
262{
263	type Identity = UncheckedExtrinsic<Address, Call, Signature, Extension>;
264
265	fn type_info() -> Type {
266		Type::builder()
267			.path(Path::new("UncheckedExtrinsic", module_path!()))
268			// Include the type parameter types, even though they are not used directly in any of
269			// the described fields. These type definitions can be used by downstream consumers
270			// to help construct the custom decoding from the opaque bytes (see below).
271			.type_params(vec![
272				TypeParameter::new("Address", Some(meta_type::<Address>())),
273				TypeParameter::new("Call", Some(meta_type::<Call>())),
274				TypeParameter::new("Signature", Some(meta_type::<Signature>())),
275				TypeParameter::new("Extra", Some(meta_type::<Extension>())),
276			])
277			.docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"])
278			// Because of the custom encoding, we can only accurately describe the encoding as an
279			// opaque `Vec<u8>`. Downstream consumers will need to manually implement the codec to
280			// encode/decode the `signature` and `function` fields.
281			.composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
282	}
283}
284
285impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize>
286	UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
287{
288	/// New instance of a bare (ne unsigned) extrinsic. This could be used for an inherent or an
289	/// old-school "unsigned transaction" (which are new being deprecated in favour of general
290	/// transactions).
291	#[deprecated = "Use new_bare instead"]
292	pub fn new_unsigned(function: Call) -> Self {
293		Self::new_bare(function)
294	}
295
296	/// Returns `true` if this extrinsic instance is an inherent, `false`` otherwise.
297	pub fn is_inherent(&self) -> bool {
298		matches!(self.preamble, Preamble::Bare(_))
299	}
300
301	/// Returns `true` if this extrinsic instance is an old-school signed transaction, `false`
302	/// otherwise.
303	pub fn is_signed(&self) -> bool {
304		matches!(self.preamble, Preamble::Signed(..))
305	}
306
307	/// Create an `UncheckedExtrinsic` from a `Preamble` and the actual `Call`.
308	pub fn from_parts(function: Call, preamble: Preamble<Address, Signature, Extension>) -> Self {
309		Self { preamble, function }
310	}
311
312	/// New instance of a bare (ne unsigned) extrinsic.
313	pub fn new_bare(function: Call) -> Self {
314		Self::from_parts(function, Preamble::Bare(EXTRINSIC_FORMAT_VERSION))
315	}
316
317	/// New instance of a bare (ne unsigned) extrinsic on extrinsic format version 4.
318	pub fn new_bare_legacy(function: Call) -> Self {
319		Self::from_parts(function, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION))
320	}
321
322	/// New instance of an old-school signed transaction on extrinsic format version 4.
323	pub fn new_signed(
324		function: Call,
325		signed: Address,
326		signature: Signature,
327		tx_ext: Extension,
328	) -> Self {
329		Self::from_parts(function, Preamble::Signed(signed, signature, tx_ext))
330	}
331
332	/// New instance of an new-school unsigned transaction.
333	pub fn new_transaction(function: Call, tx_ext: Extension) -> Self {
334		Self::from_parts(function, Preamble::General(EXTENSION_VERSION, tx_ext))
335	}
336
337	fn decode_with_len<I: Input>(input: &mut I, len: usize) -> Result<Self, codec::Error>
338	where
339		Preamble<Address, Signature, Extension>: Decode,
340		Call: DecodeWithMemTracking,
341	{
342		let mut input = CountedInput::new(input);
343
344		let preamble = Decode::decode(&mut input)?;
345		// Adds 1 byte to the `MAX_CALL_SIZE` as the decoding fails exactly at the given value and
346		// the maximum should be allowed to fit in.
347		let function = Call::decode_with_mem_limit(&mut input, MAX_CALL_SIZE.saturating_add(1))?;
348
349		if input.count() != len as u64 {
350			return Err("Invalid length prefix".into());
351		}
352
353		Ok(Self { preamble, function })
354	}
355
356	fn encode_without_prefix(&self) -> Vec<u8>
357	where
358		Preamble<Address, Signature, Extension>: Encode,
359		Call: Encode,
360	{
361		(&self.preamble, &self.function).encode()
362	}
363}
364
365impl<Address, Call, Signature, Extension> ExtrinsicLike
366	for UncheckedExtrinsic<Address, Call, Signature, Extension>
367{
368	fn is_signed(&self) -> Option<bool> {
369		Some(matches!(self.preamble, Preamble::Signed(..)))
370	}
371
372	fn is_bare(&self) -> bool {
373		matches!(self.preamble, Preamble::Bare(_))
374	}
375}
376
377impl<Address, Call, Signature, Extra> ExtrinsicCall
378	for UncheckedExtrinsic<Address, Call, Signature, Extra>
379{
380	type Call = Call;
381
382	fn call(&self) -> &Call {
383		&self.function
384	}
385
386	fn into_call(self) -> Self::Call {
387		self.function
388	}
389}
390
391// TODO: Migrate existing extension pipelines to support current `Signed` transactions as `General`
392// transactions by adding an extension to validate signatures, as they are currently validated in
393// the `Checkable` implementation for `Signed` transactions.
394
395impl<LookupSource, AccountId, Call, Signature, Extension, Lookup> Checkable<Lookup>
396	for UncheckedExtrinsic<LookupSource, Call, Signature, Extension>
397where
398	LookupSource: Member + MaybeDisplay,
399	Call: Encode + Member + Dispatchable,
400	Signature: Member + traits::Verify,
401	<Signature as traits::Verify>::Signer: IdentifyAccount<AccountId = AccountId>,
402	Extension: Encode + TransactionExtension<Call>,
403	AccountId: Member + MaybeDisplay,
404	Lookup: traits::Lookup<Source = LookupSource, Target = AccountId>,
405{
406	type Checked = CheckedExtrinsic<AccountId, Call, Extension>;
407
408	fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
409		Ok(match self.preamble {
410			Preamble::Signed(signed, signature, tx_ext) => {
411				let signed = lookup.lookup(signed)?;
412				// The `Implicit` is "implicitly" included in the payload.
413				let raw_payload = SignedPayload::new(self.function, tx_ext)?;
414				if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
415					return Err(InvalidTransaction::BadProof.into());
416				}
417				let (function, tx_ext, _) = raw_payload.deconstruct();
418				CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function }
419			},
420			Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
421				format: ExtrinsicFormat::General(extension_version, tx_ext),
422				function: self.function,
423			},
424			Preamble::Bare(_) => {
425				CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }
426			},
427		})
428	}
429
430	#[cfg(feature = "try-runtime")]
431	fn unchecked_into_checked_i_know_what_i_am_doing(
432		self,
433		lookup: &Lookup,
434	) -> Result<Self::Checked, TransactionValidityError> {
435		Ok(match self.preamble {
436			Preamble::Signed(signed, _, tx_ext) => {
437				let signed = lookup.lookup(signed)?;
438				CheckedExtrinsic {
439					format: ExtrinsicFormat::Signed(signed, tx_ext),
440					function: self.function,
441				}
442			},
443			Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
444				format: ExtrinsicFormat::General(extension_version, tx_ext),
445				function: self.function,
446			},
447			Preamble::Bare(_) => {
448				CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }
449			},
450		})
451	}
452}
453
454impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
455	ExtrinsicMetadata for UncheckedExtrinsic<Address, Call, Signature, Extension>
456{
457	const VERSIONS: &'static [u8] = &[LEGACY_EXTRINSIC_FORMAT_VERSION, EXTRINSIC_FORMAT_VERSION];
458	type TransactionExtensions = Extension;
459}
460
461impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
462	UncheckedExtrinsic<Address, Call, Signature, Extension>
463{
464	/// Returns the weight of the extension of this transaction, if present. If the transaction
465	/// doesn't use any extension, the weight returned is equal to zero.
466	pub fn extension_weight(&self) -> Weight {
467		match &self.preamble {
468			Preamble::Bare(_) => Weight::zero(),
469			Preamble::Signed(_, _, ext) | Preamble::General(_, ext) => ext.weight(&self.function),
470		}
471	}
472}
473
474impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize> Decode
475	for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
476where
477	Address: Decode,
478	Signature: Decode,
479	Call: DecodeWithMemTracking,
480	Extension: Decode,
481{
482	fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
483		// This is a little more complicated than usual since the binary format must be compatible
484		// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
485		// will be a prefix of vector length.
486		let expected_length: Compact<u32> = Decode::decode(input)?;
487
488		Self::decode_with_len(input, expected_length.0 as usize)
489	}
490}
491
492#[docify::export(unchecked_extrinsic_encode_impl)]
493impl<Address, Call, Signature, Extension> Encode
494	for UncheckedExtrinsic<Address, Call, Signature, Extension>
495where
496	Preamble<Address, Signature, Extension>: Encode,
497	Call: Encode,
498	Extension: Encode,
499{
500	fn encode(&self) -> Vec<u8> {
501		let tmp = self.encode_without_prefix();
502
503		let compact_len = codec::Compact::<u32>(tmp.len() as u32);
504
505		// Allocate the output buffer with the correct length
506		let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
507
508		compact_len.encode_to(&mut output);
509		output.extend(tmp);
510
511		output
512	}
513}
514
515impl<Address, Call, Signature, Extension> EncodeLike
516	for UncheckedExtrinsic<Address, Call, Signature, Extension>
517where
518	Address: Encode,
519	Signature: Encode,
520	Call: Encode + Dispatchable,
521	Extension: TransactionExtension<Call>,
522{
523}
524
525#[cfg(feature = "serde")]
526impl<Address: Encode, Signature: Encode, Call: Encode, Extension: Encode> serde::Serialize
527	for UncheckedExtrinsic<Address, Call, Signature, Extension>
528{
529	fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
530	where
531		S: ::serde::Serializer,
532	{
533		self.using_encoded(|bytes| seq.serialize_bytes(bytes))
534	}
535}
536
537#[cfg(feature = "serde")]
538impl<'a, Address: Decode, Signature: Decode, Call: DecodeWithMemTracking, Extension: Decode>
539	serde::Deserialize<'a> for UncheckedExtrinsic<Address, Call, Signature, Extension>
540{
541	fn deserialize<D>(de: D) -> Result<Self, D::Error>
542	where
543		D: serde::Deserializer<'a>,
544	{
545		let r = pezsp_core::bytes::deserialize(de)?;
546		Self::decode(&mut &r[..])
547			.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
548	}
549}
550
551/// A payload that has been signed for an unchecked extrinsics.
552///
553/// Note that the payload that we sign to produce unchecked extrinsic signature
554/// is going to be different than the `SignaturePayload` - so the thing the extrinsic
555/// actually contains.
556pub struct SignedPayload<Call: Dispatchable, Extension: TransactionExtension<Call>>(
557	(Call, Extension, Extension::Implicit),
558);
559
560impl<Call, Extension> SignedPayload<Call, Extension>
561where
562	Call: Encode + Dispatchable,
563	Extension: TransactionExtension<Call>,
564{
565	/// Create new `SignedPayload` for extrinsic format version 4.
566	///
567	/// This function may fail if `implicit` of `Extension` is not available.
568	pub fn new(call: Call, tx_ext: Extension) -> Result<Self, TransactionValidityError> {
569		let implicit = Extension::implicit(&tx_ext)?;
570		let raw_payload = (call, tx_ext, implicit);
571		Ok(Self(raw_payload))
572	}
573
574	/// Create new `SignedPayload` from raw components.
575	pub fn from_raw(call: Call, tx_ext: Extension, implicit: Extension::Implicit) -> Self {
576		Self((call, tx_ext, implicit))
577	}
578
579	/// Deconstruct the payload into it's components.
580	pub fn deconstruct(self) -> (Call, Extension, Extension::Implicit) {
581		self.0
582	}
583}
584
585impl<Call, Extension> Encode for SignedPayload<Call, Extension>
586where
587	Call: Encode + Dispatchable,
588	Extension: TransactionExtension<Call>,
589{
590	/// Get an encoded version of this `blake2_256`-hashed payload.
591	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
592		self.0.using_encoded(|payload| {
593			if payload.len() > 256 {
594				f(&blake2_256(payload)[..])
595			} else {
596				f(payload)
597			}
598		})
599	}
600}
601
602impl<Call, Extension> EncodeLike for SignedPayload<Call, Extension>
603where
604	Call: Encode + Dispatchable,
605	Extension: TransactionExtension<Call>,
606{
607}
608
609impl<Address, Call, Signature, Extension>
610	From<UncheckedExtrinsic<Address, Call, Signature, Extension>> for OpaqueExtrinsic
611where
612	Preamble<Address, Signature, Extension>: Encode,
613	Call: Encode,
614{
615	fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extension>) -> Self {
616		Self::from_blob(extrinsic.encode_without_prefix())
617	}
618}
619
620impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize> LazyExtrinsic
621	for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
622where
623	Preamble<Address, Signature, Extension>: Decode,
624	Call: DecodeWithMemTracking,
625{
626	fn decode_unprefixed(data: &[u8]) -> Result<Self, codec::Error> {
627		Self::decode_with_len(&mut &data[..], data.len())
628	}
629}
630
631#[cfg(test)]
632mod legacy {
633	use codec::{Compact, Decode, Encode, EncodeLike, Error, Input};
634	use scale_info::{
635		build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter,
636	};
637
638	pub type UncheckedSignaturePayloadV4<Address, Signature, Extra> = (Address, Signature, Extra);
639
640	#[derive(PartialEq, Eq, Clone, Debug)]
641	pub struct UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
642		pub signature: Option<UncheckedSignaturePayloadV4<Address, Signature, Extra>>,
643		pub function: Call,
644	}
645
646	impl<Address, Call, Signature, Extra> TypeInfo
647		for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
648	where
649		Address: StaticTypeInfo,
650		Call: StaticTypeInfo,
651		Signature: StaticTypeInfo,
652		Extra: StaticTypeInfo,
653	{
654		type Identity = UncheckedExtrinsicV4<Address, Call, Signature, Extra>;
655
656		fn type_info() -> Type {
657			Type::builder()
658				.path(Path::new("UncheckedExtrinsic", module_path!()))
659				// Include the type parameter types, even though they are not used directly in any
660				// of the described fields. These type definitions can be used by downstream
661				// consumers to help construct the custom decoding from the opaque bytes (see
662				// below).
663				.type_params(vec![
664					TypeParameter::new("Address", Some(meta_type::<Address>())),
665					TypeParameter::new("Call", Some(meta_type::<Call>())),
666					TypeParameter::new("Signature", Some(meta_type::<Signature>())),
667					TypeParameter::new("Extra", Some(meta_type::<Extra>())),
668				])
669				.docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"])
670				// Because of the custom encoding, we can only accurately describe the encoding as
671				// an opaque `Vec<u8>`. Downstream consumers will need to manually implement the
672				// codec to encode/decode the `signature` and `function` fields.
673				.composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
674		}
675	}
676
677	impl<Address, Call, Signature, Extra> UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
678		pub fn new_signed(
679			function: Call,
680			signed: Address,
681			signature: Signature,
682			extra: Extra,
683		) -> Self {
684			Self { signature: Some((signed, signature, extra)), function }
685		}
686
687		pub fn new_unsigned(function: Call) -> Self {
688			Self { signature: None, function }
689		}
690	}
691
692	impl<Address, Call, Signature, Extra> Decode
693		for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
694	where
695		Address: Decode,
696		Signature: Decode,
697		Call: Decode,
698		Extra: Decode,
699	{
700		fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
701			// This is a little more complicated than usual since the binary format must be
702			// compatible with SCALE's generic `Vec<u8>` type. Basically this just means accepting
703			// that there will be a prefix of vector length.
704			let expected_length: Compact<u32> = Decode::decode(input)?;
705			let before_length = input.remaining_len()?;
706
707			let version = input.read_byte()?;
708
709			let is_signed = version & 0b1000_0000 != 0;
710			let version = version & 0b0111_1111;
711			if version != 4u8 {
712				return Err("Invalid transaction version".into());
713			}
714
715			let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
716			let function = Decode::decode(input)?;
717
718			if let Some((before_length, after_length)) =
719				input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
720			{
721				let length = before_length.saturating_sub(after_length);
722
723				if length != expected_length.0 as usize {
724					return Err("Invalid length prefix".into());
725				}
726			}
727
728			Ok(Self { signature, function })
729		}
730	}
731
732	#[docify::export(unchecked_extrinsic_encode_impl)]
733	impl<Address, Call, Signature, Extra> Encode
734		for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
735	where
736		Address: Encode,
737		Signature: Encode,
738		Call: Encode,
739		Extra: Encode,
740	{
741		fn encode(&self) -> Vec<u8> {
742			let mut tmp = Vec::with_capacity(core::mem::size_of::<Self>());
743
744			// 1 byte version id.
745			match self.signature.as_ref() {
746				Some(s) => {
747					tmp.push(4u8 | 0b1000_0000);
748					s.encode_to(&mut tmp);
749				},
750				None => {
751					tmp.push(4u8 & 0b0111_1111);
752				},
753			}
754			self.function.encode_to(&mut tmp);
755
756			let compact_len = codec::Compact::<u32>(tmp.len() as u32);
757
758			// Allocate the output buffer with the correct length
759			let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
760
761			compact_len.encode_to(&mut output);
762			output.extend(tmp);
763
764			output
765		}
766	}
767
768	impl<Address, Call, Signature, Extra> EncodeLike
769		for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
770	where
771		Address: Encode,
772		Signature: Encode,
773		Call: Encode,
774		Extra: Encode,
775	{
776	}
777}
778
779#[cfg(test)]
780mod tests {
781	use super::{legacy::UncheckedExtrinsicV4, *};
782	use crate::{
783		codec::{Decode, Encode},
784		impl_tx_ext_default,
785		testing::TestSignature as TestSig,
786		traits::{FakeDispatchable, IdentityLookup, TransactionExtension},
787	};
788	use pezsp_io::hashing::blake2_256;
789
790	type TestContext = IdentityLookup<u64>;
791	type TestAccountId = u64;
792	type TestCall = FakeDispatchable<Vec<u8>>;
793
794	const TEST_ACCOUNT: TestAccountId = 0;
795
796	// NOTE: this is demonstration. One can simply use `()` for testing.
797	#[derive(
798		Debug,
799		Encode,
800		Decode,
801		DecodeWithMemTracking,
802		Clone,
803		Eq,
804		PartialEq,
805		Ord,
806		PartialOrd,
807		TypeInfo,
808	)]
809	struct DummyExtension;
810	impl TransactionExtension<TestCall> for DummyExtension {
811		const IDENTIFIER: &'static str = "DummyExtension";
812		type Implicit = ();
813		type Val = ();
814		type Pre = ();
815		impl_tx_ext_default!(TestCall; weight validate prepare);
816	}
817
818	type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, DummyExtension>;
819	type CEx = CheckedExtrinsic<TestAccountId, TestCall, DummyExtension>;
820
821	#[test]
822	fn unsigned_codec_should_work() {
823		let call: TestCall = vec![0u8; 0].into();
824		let ux = Ex::new_bare(call);
825		let encoded = ux.encode();
826		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
827	}
828
829	#[test]
830	fn invalid_length_prefix_is_detected() {
831		let ux = Ex::new_bare(vec![0u8; 0].into());
832		let mut encoded = ux.encode();
833
834		let length = Compact::<u32>::decode(&mut &encoded[..]).unwrap();
835		Compact(length.0 + 10).encode_to(&mut &mut encoded[..1]);
836
837		assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into()));
838	}
839
840	#[test]
841	fn transaction_codec_should_work() {
842		let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
843		let encoded = ux.encode();
844		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
845	}
846
847	#[test]
848	fn signed_codec_should_work() {
849		let ux = Ex::new_signed(
850			vec![0u8; 0].into(),
851			TEST_ACCOUNT,
852			TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()),
853			DummyExtension,
854		);
855		let encoded = ux.encode();
856		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
857	}
858
859	#[test]
860	fn large_signed_codec_should_work() {
861		let ux = Ex::new_signed(
862			vec![0u8; 0].into(),
863			TEST_ACCOUNT,
864			TestSig(
865				TEST_ACCOUNT,
866				(vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(),
867			),
868			DummyExtension,
869		);
870		let encoded = ux.encode();
871		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
872	}
873
874	#[test]
875	fn unsigned_check_should_work() {
876		let ux = Ex::new_bare(vec![0u8; 0].into());
877		assert!(ux.is_inherent());
878		assert_eq!(
879			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
880			Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }),
881		);
882	}
883
884	#[test]
885	fn badly_signed_check_should_fail() {
886		let ux = Ex::new_signed(
887			vec![0u8; 0].into(),
888			TEST_ACCOUNT,
889			TestSig(TEST_ACCOUNT, vec![0u8; 0].into()),
890			DummyExtension,
891		);
892		assert!(!ux.is_inherent());
893		assert_eq!(
894			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
895			Err(InvalidTransaction::BadProof.into()),
896		);
897	}
898
899	#[test]
900	fn transaction_check_should_work() {
901		let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
902		assert!(!ux.is_inherent());
903		assert_eq!(
904			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
905			Ok(CEx {
906				format: ExtrinsicFormat::General(0, DummyExtension),
907				function: vec![0u8; 0].into()
908			}),
909		);
910	}
911
912	#[test]
913	fn signed_check_should_work() {
914		let sig_payload = SignedPayload::from_raw(
915			FakeDispatchable::from(vec![0u8; 0]),
916			DummyExtension,
917			DummyExtension.implicit().unwrap(),
918		);
919		let ux = Ex::new_signed(
920			vec![0u8; 0].into(),
921			TEST_ACCOUNT,
922			TestSig(TEST_ACCOUNT, sig_payload.encode()),
923			DummyExtension,
924		);
925		assert!(!ux.is_inherent());
926		assert_eq!(
927			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
928			Ok(CEx {
929				format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension),
930				function: vec![0u8; 0].into()
931			}),
932		);
933	}
934
935	#[test]
936	fn encoding_matches_vec() {
937		let ex = Ex::new_bare(vec![0u8; 0].into());
938		let encoded = ex.encode();
939		let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
940		assert_eq!(decoded, ex);
941		let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
942		assert_eq!(as_vec.encode(), encoded);
943	}
944
945	#[test]
946	fn conversion_to_opaque() {
947		let ux = Ex::new_bare(vec![0u8; 0].into());
948		let encoded = ux.encode();
949		let opaque: OpaqueExtrinsic = ux.into();
950		let opaque_encoded = opaque.encode();
951		assert_eq!(opaque_encoded, encoded);
952	}
953
954	#[test]
955	fn large_bad_prefix_should_work() {
956		let encoded = (Compact::<u32>::from(u32::MAX), Preamble::<(), (), ()>::Bare(0)).encode();
957		assert!(Ex::decode(&mut &encoded[..]).is_err());
958	}
959
960	#[test]
961	fn legacy_short_signed_encode_decode() {
962		let call: TestCall = vec![0u8; 4].into();
963		let signed = TEST_ACCOUNT;
964		let extension = DummyExtension;
965		let implicit = extension.implicit().unwrap();
966		let legacy_signature = TestSig(TEST_ACCOUNT, (&call, &extension, &implicit).encode());
967
968		let old_ux =
969			UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
970				call.clone(),
971				signed,
972				legacy_signature.clone(),
973				extension.clone(),
974			);
975
976		let encoded_old_ux = old_ux.encode();
977		let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
978
979		assert_eq!(decoded_old_ux.function, call);
980		assert_eq!(
981			decoded_old_ux.preamble,
982			Preamble::Signed(signed, legacy_signature.clone(), extension.clone())
983		);
984
985		let new_ux =
986			Ex::new_signed(call.clone(), signed, legacy_signature.clone(), extension.clone());
987
988		let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
989		let old_checked =
990			decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
991		assert_eq!(new_checked, old_checked);
992	}
993
994	#[test]
995	fn legacy_long_signed_encode_decode() {
996		let call: TestCall = vec![0u8; 257].into();
997		let signed = TEST_ACCOUNT;
998		let extension = DummyExtension;
999		let implicit = extension.implicit().unwrap();
1000		let signature = TestSig(
1001			TEST_ACCOUNT,
1002			blake2_256(&(&call, DummyExtension, &implicit).encode()[..]).to_vec(),
1003		);
1004
1005		let old_ux =
1006			UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
1007				call.clone(),
1008				signed,
1009				signature.clone(),
1010				extension.clone(),
1011			);
1012
1013		let encoded_old_ux = old_ux.encode();
1014		let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1015
1016		assert_eq!(decoded_old_ux.function, call);
1017		assert_eq!(
1018			decoded_old_ux.preamble,
1019			Preamble::Signed(signed, signature.clone(), extension.clone())
1020		);
1021
1022		let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone());
1023
1024		let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1025		let old_checked =
1026			decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1027		assert_eq!(new_checked, old_checked);
1028	}
1029
1030	#[test]
1031	fn legacy_unsigned_encode_decode() {
1032		let call: TestCall = vec![0u8; 0].into();
1033
1034		let old_ux =
1035			UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_unsigned(
1036				call.clone(),
1037			);
1038
1039		let encoded_old_ux = old_ux.encode();
1040		let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1041
1042		assert_eq!(decoded_old_ux.function, call);
1043		assert_eq!(decoded_old_ux.preamble, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION));
1044
1045		let new_legacy_ux = Ex::new_bare_legacy(call.clone());
1046		assert_eq!(encoded_old_ux, new_legacy_ux.encode());
1047
1048		let new_ux = Ex::new_bare(call.clone());
1049		let encoded_new_ux = new_ux.encode();
1050		let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap();
1051		assert_eq!(new_ux, decoded_new_ux);
1052
1053		let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1054		let old_checked =
1055			decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1056		assert_eq!(new_checked, old_checked);
1057	}
1058
1059	#[test]
1060	fn max_call_heap_size_should_be_checked() {
1061		// Should be able to decode an `UncheckedExtrinsic` that contains a call with
1062		// heap size < `MAX_CALL_HEAP_SIZE`
1063		let ux = Ex::new_bare(vec![0u8; DEFAULT_MAX_CALL_SIZE].into());
1064		let encoded = ux.encode();
1065		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1066
1067		// Otherwise should fail
1068		let ux = Ex::new_bare(vec![0u8; DEFAULT_MAX_CALL_SIZE + 1].into());
1069		let encoded = ux.encode();
1070		assert_eq!(
1071			Ex::decode(&mut &encoded[..]).unwrap_err().to_string(),
1072			"Could not decode `FakeDispatchable.0`:\n\tHeap memory limit exceeded while decoding\n"
1073		);
1074	}
1075}