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