Skip to main content

tp_runtime/generic/
unchecked_extrinsic.rs

1// This file is part of Tetcore.
2
3// Copyright (C) 2017-2021 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 tetcore_std::{fmt, prelude::*};
21use tet_io::hashing::blake2_256;
22use codec::{Decode, Encode, EncodeLike, Input, Error};
23use crate::{
24	traits::{
25		self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, ExtrinsicMetadata,
26		IdentifyAccount,
27	},
28	generic::CheckedExtrinsic,
29	transaction_validity::{TransactionValidityError, InvalidTransaction},
30	OpaqueExtrinsic,
31};
32
33/// Current version of the [`UncheckedExtrinsic`] format.
34const EXTRINSIC_VERSION: u8 = 4;
35
36/// A extrinsic right from the external world. This is unchecked and so
37/// can contain a signature.
38#[derive(PartialEq, Eq, Clone)]
39pub struct UncheckedExtrinsic<Address, Call, Signature, Extra>
40where
41	Extra: SignedExtension
42{
43	/// The signature, address, number of extrinsics have come before from
44	/// the same signer and an era describing the longevity of this transaction,
45	/// if this is a signed extrinsic.
46	pub signature: Option<(Address, Signature, Extra)>,
47	/// The function that should be called.
48	pub function: Call,
49}
50
51#[cfg(feature = "std")]
52impl<Address, Call, Signature, Extra> tetsy_util_mem::MallocSizeOf
53	for UncheckedExtrinsic<Address, Call, Signature, Extra>
54where
55	Extra: SignedExtension
56{
57	fn size_of(&self, _ops: &mut tetsy_util_mem::MallocSizeOfOps) -> usize {
58		// Instantiated only in runtime.
59		0
60	}
61}
62
63impl<Address, Call, Signature, Extra: SignedExtension>
64	UncheckedExtrinsic<Address, Call, Signature, Extra>
65{
66	/// New instance of a signed extrinsic aka "transaction".
67	pub fn new_signed(
68		function: Call,
69		signed: Address,
70		signature: Signature,
71		extra: Extra
72	) -> Self {
73		UncheckedExtrinsic {
74			signature: Some((signed, signature, extra)),
75			function,
76		}
77	}
78
79	/// New instance of an unsigned extrinsic aka "inherent".
80	pub fn new_unsigned(function: Call) -> Self {
81		UncheckedExtrinsic {
82			signature: None,
83			function,
84		}
85	}
86}
87
88impl<Address, Call, Signature, Extra: SignedExtension> Extrinsic
89	for UncheckedExtrinsic<Address, Call, Signature, Extra>
90{
91	type Call = Call;
92
93	type SignaturePayload = (
94		Address,
95		Signature,
96		Extra,
97	);
98
99	fn is_signed(&self) -> Option<bool> {
100		Some(self.signature.is_some())
101	}
102
103	fn new(function: Call, signed_data: Option<Self::SignaturePayload>) -> Option<Self> {
104		Some(if let Some((address, signature, extra)) = signed_data {
105			UncheckedExtrinsic::new_signed(function, address, signature, extra)
106		} else {
107			UncheckedExtrinsic::new_unsigned(function)
108		})
109	}
110}
111
112impl<Address, AccountId, Call, Signature, Extra, Lookup>
113	Checkable<Lookup>
114for
115	UncheckedExtrinsic<Address, Call, Signature, Extra>
116where
117	Address: Member + MaybeDisplay,
118	Call: Encode + Member,
119	Signature: Member + traits::Verify,
120	<Signature as traits::Verify>::Signer: IdentifyAccount<AccountId=AccountId>,
121	Extra: SignedExtension<AccountId=AccountId>,
122	AccountId: Member + MaybeDisplay,
123	Lookup: traits::Lookup<Source=Address, Target=AccountId>,
124{
125	type Checked = CheckedExtrinsic<AccountId, Call, Extra>;
126
127	fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
128		Ok(match self.signature {
129			Some((signed, signature, extra)) => {
130				let signed = lookup.lookup(signed)?;
131				let raw_payload = SignedPayload::new(self.function, extra)?;
132				if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
133					return Err(InvalidTransaction::BadProof.into())
134				}
135
136				let (function, extra, _) = raw_payload.deconstruct();
137				CheckedExtrinsic {
138					signed: Some((signed, extra)),
139					function,
140				}
141			}
142			None => CheckedExtrinsic {
143				signed: None,
144				function: self.function,
145			},
146		})
147	}
148}
149
150impl<Address, Call, Signature, Extra> ExtrinsicMetadata
151	for UncheckedExtrinsic<Address, Call, Signature, Extra>
152		where
153			Extra: SignedExtension,
154{
155	const VERSION: u8 = EXTRINSIC_VERSION;
156	type SignedExtensions = Extra;
157}
158
159/// A payload that has been signed for an unchecked extrinsics.
160///
161/// Note that the payload that we sign to produce unchecked extrinsic signature
162/// is going to be different than the `SignaturePayload` - so the thing the extrinsic
163/// actually contains.
164pub struct SignedPayload<Call, Extra: SignedExtension>((
165	Call,
166	Extra,
167	Extra::AdditionalSigned,
168));
169
170impl<Call, Extra> SignedPayload<Call, Extra> where
171	Call: Encode,
172	Extra: SignedExtension,
173{
174	/// Create new `SignedPayload`.
175	///
176	/// This function may fail if `additional_signed` of `Extra` is not available.
177	pub fn new(call: Call, extra: Extra) -> Result<Self, TransactionValidityError> {
178		let additional_signed = extra.additional_signed()?;
179		let raw_payload = (call, extra, additional_signed);
180		Ok(Self(raw_payload))
181	}
182
183	/// Create new `SignedPayload` from raw components.
184	pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self {
185		Self((call, extra, additional_signed))
186	}
187
188	/// Deconstruct the payload into it's components.
189	pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) {
190		self.0
191	}
192}
193
194impl<Call, Extra> Encode for SignedPayload<Call, Extra> where
195	Call: Encode,
196	Extra: SignedExtension,
197{
198	/// Get an encoded version of this payload.
199	///
200	/// Payloads longer than 256 bytes are going to be `blake2_256`-hashed.
201	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
202		self.0.using_encoded(|payload| {
203			if payload.len() > 256 {
204				f(&blake2_256(payload)[..])
205			} else {
206				f(payload)
207			}
208		})
209	}
210}
211
212impl<Call, Extra> EncodeLike for SignedPayload<Call, Extra>
213where
214	Call: Encode,
215	Extra: SignedExtension,
216{}
217
218impl<Address, Call, Signature, Extra> Decode
219	for UncheckedExtrinsic<Address, Call, Signature, Extra>
220where
221	Address: Decode,
222	Signature: Decode,
223	Call: Decode,
224	Extra: SignedExtension,
225{
226	fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
227		// This is a little more complicated than usual since the binary format must be compatible
228		// with tetcore's generic `Vec<u8>` type. Basically this just means accepting that there
229		// will be a prefix of vector length (we don't need
230		// to use this).
231		let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
232
233		let version = input.read_byte()?;
234
235		let is_signed = version & 0b1000_0000 != 0;
236		let version = version & 0b0111_1111;
237		if version != EXTRINSIC_VERSION {
238			return Err("Invalid transaction version".into());
239		}
240
241		Ok(UncheckedExtrinsic {
242			signature: if is_signed { Some(Decode::decode(input)?) } else { None },
243			function: Decode::decode(input)?,
244		})
245	}
246}
247
248impl<Address, Call, Signature, Extra> Encode
249	for UncheckedExtrinsic<Address, Call, Signature, Extra>
250where
251	Address: Encode,
252	Signature: Encode,
253	Call: Encode,
254	Extra: SignedExtension,
255{
256	fn encode(&self) -> Vec<u8> {
257		super::encode_with_vec_prefix::<Self, _>(|v| {
258			// 1 byte version id.
259			match self.signature.as_ref() {
260				Some(s) => {
261					v.push(EXTRINSIC_VERSION | 0b1000_0000);
262					s.encode_to(v);
263				}
264				None => {
265					v.push(EXTRINSIC_VERSION & 0b0111_1111);
266				}
267			}
268			self.function.encode_to(v);
269		})
270	}
271}
272
273impl<Address, Call, Signature, Extra> EncodeLike
274	for UncheckedExtrinsic<Address, Call, Signature, Extra>
275where
276	Address: Encode,
277	Signature: Encode,
278	Call: Encode,
279	Extra: SignedExtension,
280{}
281
282#[cfg(feature = "std")]
283impl<Address: Encode, Signature: Encode, Call: Encode, Extra: SignedExtension> serde::Serialize
284	for UncheckedExtrinsic<Address, Call, Signature, Extra>
285{
286	fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
287		self.using_encoded(|bytes| seq.serialize_bytes(bytes))
288	}
289}
290
291#[cfg(feature = "std")]
292impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension> serde::Deserialize<'a>
293	for UncheckedExtrinsic<Address, Call, Signature, Extra>
294{
295	fn deserialize<D>(de: D) -> Result<Self, D::Error> where
296		D: serde::Deserializer<'a>,
297	{
298		let r = tet_core::bytes::deserialize(de)?;
299		Decode::decode(&mut &r[..])
300			.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
301	}
302}
303
304impl<Address, Call, Signature, Extra> fmt::Debug
305	for UncheckedExtrinsic<Address, Call, Signature, Extra>
306where
307	Address: fmt::Debug,
308	Call: fmt::Debug,
309	Extra: SignedExtension,
310{
311	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312		write!(
313			f,
314			"UncheckedExtrinsic({:?}, {:?})",
315			self.signature.as_ref().map(|x| (&x.0, &x.2)),
316			self.function,
317		)
318	}
319}
320
321impl<Address, Call, Signature, Extra> From<UncheckedExtrinsic<Address, Call, Signature, Extra>>
322	for OpaqueExtrinsic
323where
324	Address: Encode,
325	Signature: Encode,
326	Call: Encode,
327	Extra: SignedExtension,
328{
329	fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extra>) -> Self {
330		OpaqueExtrinsic::from_bytes(extrinsic.encode().as_slice())
331			.expect(
332				"both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \
333				raw Vec<u8> encoding; qed"
334			)
335	}
336}
337
338#[cfg(test)]
339mod tests {
340	use super::*;
341	use tet_io::hashing::blake2_256;
342	use crate::codec::{Encode, Decode};
343	use crate::traits::{SignedExtension, IdentityLookup};
344	use crate::testing::TestSignature as TestSig;
345
346	type TestContext = IdentityLookup<u64>;
347	type TestAccountId = u64;
348	type TestCall = Vec<u8>;
349
350	const TEST_ACCOUNT: TestAccountId = 0;
351
352	// NOTE: this is demonstration. One can simply use `()` for testing.
353	#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)]
354	struct TestExtra;
355	impl SignedExtension for TestExtra {
356		const IDENTIFIER: &'static str = "TestExtra";
357		type AccountId = u64;
358		type Call = ();
359		type AdditionalSigned = ();
360		type Pre = ();
361
362		fn additional_signed(&self) -> tetcore_std::result::Result<(), TransactionValidityError> { Ok(()) }
363	}
364
365	type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, TestExtra>;
366	type CEx = CheckedExtrinsic<TestAccountId, TestCall, TestExtra>;
367
368	#[test]
369	fn unsigned_codec_should_work() {
370		let ux = Ex::new_unsigned(vec![0u8; 0]);
371		let encoded = ux.encode();
372		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
373	}
374
375	#[test]
376	fn signed_codec_should_work() {
377		let ux = Ex::new_signed(
378			vec![0u8; 0],
379			TEST_ACCOUNT,
380			TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
381			TestExtra
382		);
383		let encoded = ux.encode();
384		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
385	}
386
387	#[test]
388	fn large_signed_codec_should_work() {
389		let ux = Ex::new_signed(
390			vec![0u8; 0],
391			TEST_ACCOUNT,
392			TestSig(TEST_ACCOUNT, (vec![0u8; 257], TestExtra)
393				.using_encoded(blake2_256)[..].to_owned()),
394			TestExtra
395		);
396		let encoded = ux.encode();
397		assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
398	}
399
400	#[test]
401	fn unsigned_check_should_work() {
402		let ux = Ex::new_unsigned(vec![0u8; 0]);
403		assert!(!ux.is_signed().unwrap_or(false));
404		assert!(<Ex as Checkable<TestContext>>::check(ux, &Default::default()).is_ok());
405	}
406
407	#[test]
408	fn badly_signed_check_should_fail() {
409		let ux = Ex::new_signed(
410			vec![0u8; 0],
411			TEST_ACCOUNT,
412			TestSig(TEST_ACCOUNT, vec![0u8; 0]),
413			TestExtra,
414		);
415		assert!(ux.is_signed().unwrap_or(false));
416		assert_eq!(
417			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
418			Err(InvalidTransaction::BadProof.into()),
419		);
420	}
421
422	#[test]
423	fn signed_check_should_work() {
424		let ux = Ex::new_signed(
425			vec![0u8; 0],
426			TEST_ACCOUNT,
427			TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
428			TestExtra,
429		);
430		assert!(ux.is_signed().unwrap_or(false));
431		assert_eq!(
432			<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
433			Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }),
434		);
435	}
436
437	#[test]
438	fn encoding_matches_vec() {
439		let ex = Ex::new_unsigned(vec![0u8; 0]);
440		let encoded = ex.encode();
441		let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
442		assert_eq!(decoded, ex);
443		let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
444		assert_eq!(as_vec.encode(), encoded);
445	}
446
447	#[test]
448	fn conversion_to_opaque() {
449		let ux = Ex::new_unsigned(vec![0u8; 0]);
450		let encoded = ux.encode();
451		let opaque: OpaqueExtrinsic = ux.into();
452		let opaque_encoded = opaque.encode();
453		assert_eq!(opaque_encoded, encoded);
454	}
455}