pallet_verify_signature/extension.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//! Transaction extension which validates a signature against a payload constructed from a call and
19//! the rest of the transaction extension pipeline.
20
21use crate::{Config, WeightInfo};
22use codec::{Decode, DecodeWithMemTracking, Encode};
23use frame_support::{pallet_prelude::TransactionSource, traits::OriginTrait};
24use scale_info::TypeInfo;
25use sp_io::hashing::blake2_256;
26use sp_runtime::{
27 impl_tx_ext_default,
28 traits::{
29 transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin, DispatchInfoOf,
30 Dispatchable, Verify,
31 },
32 transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
33};
34use sp_weights::Weight;
35
36/// Extension that, if enabled, validates a signature type against the payload constructed from the
37/// call and the rest of the transaction extension pipeline. This extension provides the
38/// functionality that traditionally signed transactions had with the implicit signature checking
39/// implemented in [`Checkable`](sp_runtime::traits::Checkable). It is meant to be placed ahead of
40/// any other extensions that do authorization work in the [`TransactionExtension`] pipeline.
41#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
42#[scale_info(skip_type_params(T))]
43pub enum VerifySignature<T>
44where
45 T: Config + Send + Sync,
46{
47 /// The extension will verify the signature and, if successful, authorize a traditionally
48 /// signed transaction.
49 Signed {
50 /// The signature provided by the transaction submitter.
51 signature: T::Signature,
52 /// The account that signed the payload.
53 account: T::AccountId,
54 },
55 /// The extension is disabled and will be passthrough.
56 Disabled,
57}
58
59impl<T> core::fmt::Debug for VerifySignature<T>
60where
61 T: Config + Send + Sync,
62{
63 #[cfg(feature = "std")]
64 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
65 write!(f, "VerifySignature")
66 }
67
68 #[cfg(not(feature = "std"))]
69 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
70 Ok(())
71 }
72}
73
74impl<T> VerifySignature<T>
75where
76 T: Config + Send + Sync,
77{
78 /// Create a new extension instance that will validate the provided signature.
79 pub fn new_with_signature(signature: T::Signature, account: T::AccountId) -> Self {
80 Self::Signed { signature, account }
81 }
82
83 /// Create a new passthrough extension instance.
84 pub fn new_disabled() -> Self {
85 Self::Disabled
86 }
87}
88
89impl<T> TransactionExtension<T::RuntimeCall> for VerifySignature<T>
90where
91 T: Config + Send + Sync,
92 <T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
93{
94 const IDENTIFIER: &'static str = "VerifyMultiSignature";
95 type Implicit = ();
96 type Val = ();
97 type Pre = ();
98
99 fn weight(&self, _call: &T::RuntimeCall) -> Weight {
100 match &self {
101 // The benchmarked weight of the payload construction and signature checking.
102 Self::Signed { .. } => T::WeightInfo::verify_signature(),
103 // When the extension is passthrough, it consumes no weight.
104 Self::Disabled => Weight::zero(),
105 }
106 }
107
108 fn validate(
109 &self,
110 mut origin: <T::RuntimeCall as Dispatchable>::RuntimeOrigin,
111 _call: &T::RuntimeCall,
112 _info: &DispatchInfoOf<T::RuntimeCall>,
113 _len: usize,
114 _: (),
115 inherited_implication: &impl Encode,
116 _source: TransactionSource,
117 ) -> Result<
118 (ValidTransaction, Self::Val, <T::RuntimeCall as Dispatchable>::RuntimeOrigin),
119 TransactionValidityError,
120 > {
121 // If the extension is disabled, return early.
122 let (signature, account) = match &self {
123 Self::Signed { signature, account } => (signature, account),
124 Self::Disabled => return Ok((Default::default(), (), origin)),
125 };
126
127 // This extension must receive an unauthorized origin as it is meant to headline the
128 // authorization extension pipeline. Any extensions that precede this one must not authorize
129 // any origin and serve some other functional purpose.
130 if origin.is_transaction_authorized() {
131 return Err(InvalidTransaction::BadSigner.into());
132 }
133
134 // Construct the payload that the signature will be validated against. The inherited
135 // implication contains the encoded bytes of the call and all of the extension data of the
136 // extensions that follow in the `TransactionExtension` pipeline.
137 //
138 // In other words:
139 // - extensions that precede this extension are ignored in terms of signature validation;
140 // - extensions that follow this extension are included in the payload to be signed (as if
141 // they were the entire `SignedExtension` pipeline in the traditional signed transaction
142 // model).
143 //
144 // The encoded bytes of the payload are then hashed using `blake2_256`.
145 let msg = inherited_implication.using_encoded(blake2_256);
146
147 // The extension was enabled, so the signature must match.
148 if !signature.verify(&msg[..], account) {
149 Err(InvalidTransaction::BadProof)?
150 }
151
152 // Return the signer as the transaction origin.
153 origin.set_caller_from_signed(account.clone());
154 Ok((ValidTransaction::default(), (), origin))
155 }
156
157 impl_tx_ext_default!(T::RuntimeCall; prepare);
158}