sp_runtime/generic/
unchecked_extrinsic.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//! Generic implementation of an unchecked (pre-verification) extrinsic.
19
20use crate::{
21	generic::CheckedExtrinsic,
22	traits::{
23		self, Checkable, Extrinsic, ExtrinsicMetadata, IdentifyAccount, MaybeDisplay, Member,
24		SignaturePayload, SignedExtension,
25	},
26	transaction_validity::{InvalidTransaction, TransactionValidityError},
27	OpaqueExtrinsic,
28};
29#[cfg(all(not(feature = "std"), feature = "serde"))]
30use alloc::format;
31use alloc::{vec, vec::Vec};
32use codec::{Compact, Decode, Encode, EncodeLike, Error, Input};
33use core::fmt;
34use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter};
35use sp_io::hashing::blake2_256;
36
37/// Current version of the [`UncheckedExtrinsic`] encoded format.
38///
39/// This version needs to be bumped if the encoded representation changes.
40/// It ensures that if the representation is changed and the format is not known,
41/// the decoding fails.
42const EXTRINSIC_FORMAT_VERSION: u8 = 4;
43
44/// The `SignaturePayload` of `UncheckedExtrinsic`.
45type UncheckedSignaturePayload<Address, Signature, Extra> = (Address, Signature, Extra);
46
47/// An extrinsic right from the external world. This is unchecked and so can contain a signature.
48///
49/// An extrinsic is formally described as any external data that is originating from the outside of
50/// the runtime and fed into the runtime as a part of the block-body.
51///
52/// Inherents are special types of extrinsics that are placed into the block by the block-builder.
53/// They are unsigned because the assertion is that they are "inherently true" by virtue of getting
54/// past all validators.
55///
56/// Transactions are all other statements provided by external entities that the chain deems values
57/// and decided to include in the block. This value is typically in the form of fee payment, but it
58/// could in principle be any other interaction. Transactions are either signed or unsigned. A
59/// sensible transaction pool should ensure that only transactions that are worthwhile are
60/// considered for block-building.
61#[cfg_attr(all(feature = "std", not(windows)), doc = simple_mermaid::mermaid!("../../docs/mermaid/extrinsics.mmd"))]
62/// This type is by no means enforced within Substrate, but given its genericness, it is highly
63/// likely that for most use-cases it will suffice. Thus, the encoding of this type will dictate
64/// exactly what bytes should be sent to a runtime to transact with it.
65///
66/// This can be checked using [`Checkable`], yielding a [`CheckedExtrinsic`], which is the
67/// counterpart of this type after its signature (and other non-negotiable validity checks) have
68/// passed.
69#[derive(PartialEq, Eq, Clone)]
70pub struct UncheckedExtrinsic<Address, Call, Signature, Extra>
71where
72	Extra: SignedExtension,
73{
74	/// The signature, address, number of extrinsics have come before from the same signer and an
75	/// era describing the longevity of this transaction, if this is a signed extrinsic.
76	///
77	/// `None` if it is unsigned or an inherent.
78	pub signature: Option<UncheckedSignaturePayload<Address, Signature, Extra>>,
79	/// The function that should be called.
80	pub function: Call,
81}
82
83impl<Address: TypeInfo, Signature: TypeInfo, Extra: TypeInfo> SignaturePayload
84	for UncheckedSignaturePayload<Address, Signature, Extra>
85{
86	type SignatureAddress = Address;
87	type Signature = Signature;
88	type SignatureExtra = Extra;
89}
90
91/// Manual [`TypeInfo`] implementation because of custom encoding. The data is a valid encoded
92/// `Vec<u8>`, but requires some logic to extract the signature and payload.
93///
94/// See [`UncheckedExtrinsic::encode`] and [`UncheckedExtrinsic::decode`].
95impl<Address, Call, Signature, Extra> TypeInfo
96	for UncheckedExtrinsic<Address, Call, Signature, Extra>
97where
98	Address: StaticTypeInfo,
99	Call: StaticTypeInfo,
100	Signature: StaticTypeInfo,
101	Extra: SignedExtension + StaticTypeInfo,
102{
103	type Identity = UncheckedExtrinsic<Address, Call, Signature, Extra>;
104
105	fn type_info() -> Type {
106		Type::builder()
107			.path(Path::new("UncheckedExtrinsic", module_path!()))
108			// Include the type parameter types, even though they are not used directly in any of
109			// the described fields. These type definitions can be used by downstream consumers
110			// to help construct the custom decoding from the opaque bytes (see below).
111			.type_params(vec![
112				TypeParameter::new("Address", Some(meta_type::<Address>())),
113				TypeParameter::new("Call", Some(meta_type::<Call>())),
114				TypeParameter::new("Signature", Some(meta_type::<Signature>())),
115				TypeParameter::new("Extra", Some(meta_type::<Extra>())),
116			])
117			.docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"])
118			// Because of the custom encoding, we can only accurately describe the encoding as an
119			// opaque `Vec<u8>`. Downstream consumers will need to manually implement the codec to
120			// encode/decode the `signature` and `function` fields.
121			.composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
122	}
123}
124
125impl<Address, Call, Signature, Extra: SignedExtension>
126	UncheckedExtrinsic<Address, Call, Signature, Extra>
127{
128	/// New instance of a signed extrinsic aka "transaction".
129	pub fn new_signed(function: Call, signed: Address, signature: Signature, extra: Extra) -> Self {
130		Self { signature: Some((signed, signature, extra)), function }
131	}
132
133	/// New instance of an unsigned extrinsic aka "inherent".
134	pub fn new_unsigned(function: Call) -> Self {
135		Self { signature: None, function }
136	}
137}
138
139impl<Address: TypeInfo, Call: TypeInfo, Signature: TypeInfo, Extra: SignedExtension + TypeInfo>
140	Extrinsic for UncheckedExtrinsic<Address, Call, Signature, Extra>
141{
142	type Call = Call;
143
144	type SignaturePayload = UncheckedSignaturePayload<Address, Signature, Extra>;
145
146	fn is_signed(&self) -> Option<bool> {
147		Some(self.signature.is_some())
148	}
149
150	fn new(function: Call, signed_data: Option<Self::SignaturePayload>) -> Option<Self> {
151		Some(if let Some((address, signature, extra)) = signed_data {
152			Self::new_signed(function, address, signature, extra)
153		} else {
154			Self::new_unsigned(function)
155		})
156	}
157}
158
159impl<LookupSource, AccountId, Call, Signature, Extra, Lookup> Checkable<Lookup>
160	for UncheckedExtrinsic<LookupSource, Call, Signature, Extra>
161where
162	LookupSource: Member + MaybeDisplay,
163	Call: Encode + Member,
164	Signature: Member + traits::Verify,
165	<Signature as traits::Verify>::Signer: IdentifyAccount<AccountId = AccountId>,
166	Extra: SignedExtension<AccountId = AccountId>,
167	AccountId: Member + MaybeDisplay,
168	Lookup: traits::Lookup<Source = LookupSource, Target = AccountId>,
169{
170	type Checked = CheckedExtrinsic<AccountId, Call, Extra>;
171
172	fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
173		Ok(match self.signature {
174			Some((signed, signature, extra)) => {
175				let signed = lookup.lookup(signed)?;
176				let raw_payload = SignedPayload::new(self.function, extra)?;
177				if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
178					return Err(InvalidTransaction::BadProof.into())
179				}
180
181				let (function, extra, _) = raw_payload.deconstruct();
182				CheckedExtrinsic { signed: Some((signed, extra)), function }
183			},
184			None => CheckedExtrinsic { signed: None, function: self.function },
185		})
186	}
187
188	#[cfg(feature = "try-runtime")]
189	fn unchecked_into_checked_i_know_what_i_am_doing(
190		self,
191		lookup: &Lookup,
192	) -> Result<Self::Checked, TransactionValidityError> {
193		Ok(match self.signature {
194			Some((signed, _, extra)) => {
195				let signed = lookup.lookup(signed)?;
196				let raw_payload = SignedPayload::new(self.function, extra)?;
197				let (function, extra, _) = raw_payload.deconstruct();
198				CheckedExtrinsic { signed: Some((signed, extra)), function }
199			},
200			None => CheckedExtrinsic { signed: None, function: self.function },
201		})
202	}
203}
204
205impl<Address, Call, Signature, Extra> ExtrinsicMetadata
206	for UncheckedExtrinsic<Address, Call, Signature, Extra>
207where
208	Extra: SignedExtension,
209{
210	const VERSION: u8 = EXTRINSIC_FORMAT_VERSION;
211	type SignedExtensions = Extra;
212}
213
214/// A payload that has been signed for an unchecked extrinsics.
215///
216/// Note that the payload that we sign to produce unchecked extrinsic signature
217/// is going to be different than the `SignaturePayload` - so the thing the extrinsic
218/// actually contains.
219pub struct SignedPayload<Call, Extra: SignedExtension>((Call, Extra, Extra::AdditionalSigned));
220
221impl<Call, Extra> SignedPayload<Call, Extra>
222where
223	Call: Encode,
224	Extra: SignedExtension,
225{
226	/// Create new `SignedPayload`.
227	///
228	/// This function may fail if `additional_signed` of `Extra` is not available.
229	pub fn new(call: Call, extra: Extra) -> Result<Self, TransactionValidityError> {
230		let additional_signed = extra.additional_signed()?;
231		let raw_payload = (call, extra, additional_signed);
232		Ok(Self(raw_payload))
233	}
234
235	/// Create new `SignedPayload` from raw components.
236	pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self {
237		Self((call, extra, additional_signed))
238	}
239
240	/// Deconstruct the payload into it's components.
241	pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) {
242		self.0
243	}
244}
245
246impl<Call, Extra> Encode for SignedPayload<Call, Extra>
247where
248	Call: Encode,
249	Extra: SignedExtension,
250{
251	/// Get an encoded version of this payload.
252	///
253	/// Payloads longer than 256 bytes are going to be `blake2_256`-hashed.
254	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
255		self.0.using_encoded(|payload| {
256			if payload.len() > 256 {
257				f(&blake2_256(payload)[..])
258			} else {
259				f(payload)
260			}
261		})
262	}
263}
264
265impl<Call, Extra> EncodeLike for SignedPayload<Call, Extra>
266where
267	Call: Encode,
268	Extra: SignedExtension,
269{
270}
271
272impl<Address, Call, Signature, Extra> Decode for UncheckedExtrinsic<Address, Call, Signature, Extra>
273where
274	Address: Decode,
275	Signature: Decode,
276	Call: Decode,
277	Extra: SignedExtension,
278{
279	fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
280		// This is a little more complicated than usual since the binary format must be compatible
281		// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
282		// will be a prefix of vector length.
283		let expected_length: Compact<u32> = Decode::decode(input)?;
284		let before_length = input.remaining_len()?;
285
286		let version = input.read_byte()?;
287
288		let is_signed = version & 0b1000_0000 != 0;
289		let version = version & 0b0111_1111;
290		if version != EXTRINSIC_FORMAT_VERSION {
291			return Err("Invalid transaction version".into())
292		}
293
294		let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
295		let function = Decode::decode(input)?;
296
297		if let Some((before_length, after_length)) =
298			input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
299		{
300			let length = before_length.saturating_sub(after_length);
301
302			if length != expected_length.0 as usize {
303				return Err("Invalid length prefix".into())
304			}
305		}
306
307		Ok(Self { signature, function })
308	}
309}
310
311#[docify::export(unchecked_extrinsic_encode_impl)]
312impl<Address, Call, Signature, Extra> Encode for UncheckedExtrinsic<Address, Call, Signature, Extra>
313where
314	Address: Encode,
315	Signature: Encode,
316	Call: Encode,
317	Extra: SignedExtension,
318{
319	fn encode(&self) -> Vec<u8> {
320		let mut tmp = Vec::with_capacity(core::mem::size_of::<Self>());
321
322		// 1 byte version id.
323		match self.signature.as_ref() {
324			Some(s) => {
325				tmp.push(EXTRINSIC_FORMAT_VERSION | 0b1000_0000);
326				s.encode_to(&mut tmp);
327			},
328			None => {
329				tmp.push(EXTRINSIC_FORMAT_VERSION & 0b0111_1111);
330			},
331		}
332		self.function.encode_to(&mut tmp);
333
334		let compact_len = codec::Compact::<u32>(tmp.len() as u32);
335
336		// Allocate the output buffer with the correct length
337		let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
338
339		compact_len.encode_to(&mut output);
340		output.extend(tmp);
341
342		output
343	}
344}
345
346impl<Address, Call, Signature, Extra> EncodeLike
347	for UncheckedExtrinsic<Address, Call, Signature, Extra>
348where
349	Address: Encode,
350	Signature: Encode,
351	Call: Encode,
352	Extra: SignedExtension,
353{
354}
355
356#[cfg(feature = "serde")]
357impl<Address: Encode, Signature: Encode, Call: Encode, Extra: SignedExtension> serde::Serialize
358	for UncheckedExtrinsic<Address, Call, Signature, Extra>
359{
360	fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
361	where
362		S: ::serde::Serializer,
363	{
364		self.using_encoded(|bytes| seq.serialize_bytes(bytes))
365	}
366}
367
368#[cfg(feature = "serde")]
369impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension>
370	serde::Deserialize<'a> for UncheckedExtrinsic<Address, Call, Signature, Extra>
371{
372	fn deserialize<D>(de: D) -> Result<Self, D::Error>
373	where
374		D: serde::Deserializer<'a>,
375	{
376		let r = sp_core::bytes::deserialize(de)?;
377		Decode::decode(&mut &r[..])
378			.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
379	}
380}
381
382impl<Address, Call, Signature, Extra> fmt::Debug
383	for UncheckedExtrinsic<Address, Call, Signature, Extra>
384where
385	Address: fmt::Debug,
386	Call: fmt::Debug,
387	Extra: SignedExtension,
388{
389	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
390		write!(
391			f,
392			"UncheckedExtrinsic({:?}, {:?})",
393			self.signature.as_ref().map(|x| (&x.0, &x.2)),
394			self.function,
395		)
396	}
397}
398
399impl<Address, Call, Signature, Extra> From<UncheckedExtrinsic<Address, Call, Signature, Extra>>
400	for OpaqueExtrinsic
401where
402	Address: Encode,
403	Signature: Encode,
404	Call: Encode,
405	Extra: SignedExtension,
406{
407	fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extra>) -> Self {
408		Self::from_bytes(extrinsic.encode().as_slice()).expect(
409			"both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \
410				raw Vec<u8> encoding; qed",
411		)
412	}
413}
414
415#[cfg(test)]
416mod tests {
417	use super::*;
418	use crate::{
419		codec::{Decode, Encode},
420		testing::TestSignature as TestSig,
421		traits::{DispatchInfoOf, IdentityLookup, SignedExtension},
422	};
423	use sp_io::hashing::blake2_256;
424
425	type TestContext = IdentityLookup<u64>;
426	type TestAccountId = u64;
427	type TestCall = Vec<u8>;
428
429	const TEST_ACCOUNT: TestAccountId = 0;
430
431	// NOTE: this is demonstration. One can simply use `()` for testing.
432	#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
433	struct TestExtra;
434	impl SignedExtension for TestExtra {
435		const IDENTIFIER: &'static str = "TestExtra";
436		type AccountId = u64;
437		type Call = ();
438		type AdditionalSigned = ();
439		type Pre = ();
440
441		fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> {
442			Ok(())
443		}
444
445		fn pre_dispatch(
446			self,
447			who: &Self::AccountId,
448			call: &Self::Call,
449			info: &DispatchInfoOf<Self::Call>,
450			len: usize,
451		) -> Result<Self::Pre, TransactionValidityError> {
452			self.validate(who, call, info, len).map(|_| ())
453		}
454	}
455
456	type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, TestExtra>;
457	type CEx = CheckedExtrinsic<TestAccountId, TestCall, TestExtra>;
458
459	#[test]
460	fn unsigned_codec_should_work() {
461		let ux = Ex::new_unsigned(vec![0u8; 0]);
462		let encoded = ux.encode();
463		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
464	}
465
466	#[test]
467	fn invalid_length_prefix_is_detected() {
468		let ux = Ex::new_unsigned(vec![0u8; 0]);
469		let mut encoded = ux.encode();
470
471		let length = Compact::<u32>::decode(&mut &encoded[..]).unwrap();
472		Compact(length.0 + 10).encode_to(&mut &mut encoded[..1]);
473
474		assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into()));
475	}
476
477	#[test]
478	fn signed_codec_should_work() {
479		let ux = Ex::new_signed(
480			vec![0u8; 0],
481			TEST_ACCOUNT,
482			TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
483			TestExtra,
484		);
485		let encoded = ux.encode();
486		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
487	}
488
489	#[test]
490	fn large_signed_codec_should_work() {
491		let ux = Ex::new_signed(
492			vec![0u8; 0],
493			TEST_ACCOUNT,
494			TestSig(
495				TEST_ACCOUNT,
496				(vec![0u8; 257], TestExtra).using_encoded(blake2_256)[..].to_owned(),
497			),
498			TestExtra,
499		);
500		let encoded = ux.encode();
501		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
502	}
503
504	#[test]
505	fn unsigned_check_should_work() {
506		let ux = Ex::new_unsigned(vec![0u8; 0]);
507		assert!(!ux.is_signed().unwrap_or(false));
508		assert!(<Ex as Checkable<TestContext>>::check(ux, &Default::default()).is_ok());
509	}
510
511	#[test]
512	fn badly_signed_check_should_fail() {
513		let ux = Ex::new_signed(
514			vec![0u8; 0],
515			TEST_ACCOUNT,
516			TestSig(TEST_ACCOUNT, vec![0u8; 0]),
517			TestExtra,
518		);
519		assert!(ux.is_signed().unwrap_or(false));
520		assert_eq!(
521			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
522			Err(InvalidTransaction::BadProof.into()),
523		);
524	}
525
526	#[test]
527	fn signed_check_should_work() {
528		let ux = Ex::new_signed(
529			vec![0u8; 0],
530			TEST_ACCOUNT,
531			TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
532			TestExtra,
533		);
534		assert!(ux.is_signed().unwrap_or(false));
535		assert_eq!(
536			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
537			Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }),
538		);
539	}
540
541	#[test]
542	fn encoding_matches_vec() {
543		let ex = Ex::new_unsigned(vec![0u8; 0]);
544		let encoded = ex.encode();
545		let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
546		assert_eq!(decoded, ex);
547		let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
548		assert_eq!(as_vec.encode(), encoded);
549	}
550
551	#[test]
552	fn conversion_to_opaque() {
553		let ux = Ex::new_unsigned(vec![0u8; 0]);
554		let encoded = ux.encode();
555		let opaque: OpaqueExtrinsic = ux.into();
556		let opaque_encoded = opaque.encode();
557		assert_eq!(opaque_encoded, encoded);
558	}
559
560	#[test]
561	fn large_bad_prefix_should_work() {
562		let encoded = Compact::<u32>::from(u32::MAX).encode();
563		assert_eq!(
564			Ex::decode(&mut &encoded[..]),
565			Err(Error::from("Not enough data to fill buffer"))
566		);
567	}
568}