referrerpolicy=no-referrer-when-downgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Transaction extension which validates a signature against a payload constructed from a call and
//! the rest of the transaction extension pipeline.

use crate::{Config, WeightInfo};
use codec::{Decode, Encode};
use frame_support::traits::OriginTrait;
use scale_info::TypeInfo;
use sp_io::hashing::blake2_256;
use sp_runtime::{
	impl_tx_ext_default,
	traits::{
		transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin, DispatchInfoOf,
		Dispatchable, Verify,
	},
	transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
};
use sp_weights::Weight;

/// Extension that, if enabled, validates a signature type against the payload constructed from the
/// call and the rest of the transaction extension pipeline. This extension provides the
/// functionality that traditionally signed transactions had with the implicit signature checking
/// implemented in [`Checkable`](sp_runtime::traits::Checkable). It is meant to be placed ahead of
/// any other extensions that do authorization work in the [`TransactionExtension`] pipeline.
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub enum VerifySignature<T>
where
	T: Config + Send + Sync,
{
	/// The extension will verify the signature and, if successful, authorize a traditionally
	/// signed transaction.
	Signed {
		/// The signature provided by the transaction submitter.
		signature: T::Signature,
		/// The account that signed the payload.
		account: T::AccountId,
	},
	/// The extension is disabled and will be passthrough.
	Disabled,
}

impl<T> core::fmt::Debug for VerifySignature<T>
where
	T: Config + Send + Sync,
{
	#[cfg(feature = "std")]
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		write!(f, "VerifySignature")
	}

	#[cfg(not(feature = "std"))]
	fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
		Ok(())
	}
}

impl<T> VerifySignature<T>
where
	T: Config + Send + Sync,
{
	/// Create a new extension instance that will validate the provided signature.
	pub fn new_with_signature(signature: T::Signature, account: T::AccountId) -> Self {
		Self::Signed { signature, account }
	}

	/// Create a new passthrough extension instance.
	pub fn new_disabled() -> Self {
		Self::Disabled
	}
}

impl<T> TransactionExtension<T::RuntimeCall> for VerifySignature<T>
where
	T: Config + Send + Sync,
	<T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
{
	const IDENTIFIER: &'static str = "VerifyMultiSignature";
	type Implicit = ();
	type Val = ();
	type Pre = ();

	fn weight(&self, _call: &T::RuntimeCall) -> Weight {
		match &self {
			// The benchmarked weight of the payload construction and signature checking.
			Self::Signed { .. } => T::WeightInfo::verify_signature(),
			// When the extension is passthrough, it consumes no weight.
			Self::Disabled => Weight::zero(),
		}
	}

	fn validate(
		&self,
		mut origin: <T::RuntimeCall as Dispatchable>::RuntimeOrigin,
		_call: &T::RuntimeCall,
		_info: &DispatchInfoOf<T::RuntimeCall>,
		_len: usize,
		_: (),
		inherited_implication: &impl Encode,
	) -> Result<
		(ValidTransaction, Self::Val, <T::RuntimeCall as Dispatchable>::RuntimeOrigin),
		TransactionValidityError,
	> {
		// If the extension is disabled, return early.
		let (signature, account) = match &self {
			Self::Signed { signature, account } => (signature, account),
			Self::Disabled => return Ok((Default::default(), (), origin)),
		};

		// This extension must receive an unauthorized origin as it is meant to headline the
		// authorization extension pipeline. Any extensions that precede this one must not authorize
		// any origin and serve some other functional purpose.
		if origin.is_transaction_authorized() {
			return Err(InvalidTransaction::BadSigner.into());
		}

		// Construct the payload that the signature will be validated against. The inherited
		// implication contains the encoded bytes of the call and all of the extension data of the
		// extensions that follow in the `TransactionExtension` pipeline.
		//
		// In other words:
		// - extensions that precede this extension are ignored in terms of signature validation;
		// - extensions that follow this extension are included in the payload to be signed (as if
		//   they were the entire `SignedExtension` pipeline in the traditional signed transaction
		//   model).
		//
		// The encoded bytes of the payload are then hashed using `blake2_256`.
		let msg = inherited_implication.using_encoded(blake2_256);

		// The extension was enabled, so the signature must match.
		if !signature.verify(&msg[..], account) {
			Err(InvalidTransaction::BadProof)?
		}

		// Return the signer as the transaction origin.
		origin.set_caller_from_signed(account.clone());
		Ok((ValidTransaction::default(), (), origin))
	}

	impl_tx_ext_default!(T::RuntimeCall; prepare);
}