referrerpolicy=no-referrer-when-downgrade

substrate_test_runtime/
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//! Provides utils for building the `Extrinsic` instances used with `substrate-test-runtime`.
19
20use crate::{
21	substrate_test_pallet::pallet::Call as PalletCall, AccountId, Balance, BalancesCall,
22	CheckSubstrateCall, Extrinsic, Nonce, Pair, RuntimeCall, SignedPayload, TransferData,
23};
24use codec::Encode;
25use frame_metadata_hash_extension::CheckMetadataHash;
26use frame_system::{CheckNonce, CheckWeight};
27use sp_core::crypto::Pair as TraitPair;
28use sp_keyring::Sr25519Keyring;
29use sp_runtime::{
30	generic::Preamble, traits::TransactionExtension, transaction_validity::TransactionPriority,
31	Perbill,
32};
33
34/// Transfer used in test substrate pallet. Extrinsic is created and signed using this data.
35#[derive(Clone)]
36pub struct Transfer {
37	/// Transfer sender and signer of created extrinsic
38	pub from: Pair,
39	/// The recipient of the transfer
40	pub to: AccountId,
41	/// Amount of transfer
42	pub amount: Balance,
43	/// Sender's account nonce at which transfer is valid
44	pub nonce: u64,
45}
46
47impl Transfer {
48	/// Convert into a signed unchecked extrinsic.
49	pub fn into_unchecked_extrinsic(self) -> Extrinsic {
50		ExtrinsicBuilder::new_transfer(self).build()
51	}
52}
53
54impl Default for TransferData {
55	fn default() -> Self {
56		Self {
57			from: Sr25519Keyring::Alice.into(),
58			to: Sr25519Keyring::Bob.into(),
59			amount: 0,
60			nonce: 0,
61		}
62	}
63}
64
65/// If feasible converts given `Extrinsic` to `TransferData`
66impl TryFrom<&Extrinsic> for TransferData {
67	type Error = ();
68	fn try_from(uxt: &Extrinsic) -> Result<Self, Self::Error> {
69		match uxt {
70			Extrinsic {
71				function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }),
72				preamble: Preamble::Signed(from, _, ((CheckNonce(nonce), ..), ..)),
73				..
74			} => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }),
75			Extrinsic {
76				function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }),
77				preamble: Preamble::Bare(_),
78				..
79			} => Ok(transfer.clone()),
80			_ => Err(()),
81		}
82	}
83}
84
85/// Generates `Extrinsic`
86pub struct ExtrinsicBuilder {
87	function: RuntimeCall,
88	signer: Option<Pair>,
89	nonce: Option<Nonce>,
90	metadata_hash: Option<[u8; 32]>,
91}
92
93impl ExtrinsicBuilder {
94	/// Create builder for given `RuntimeCall`. By default `Extrinsic` will be signed by `Alice`.
95	pub fn new(function: impl Into<RuntimeCall>) -> Self {
96		Self {
97			function: function.into(),
98			signer: Some(Sr25519Keyring::Alice.pair()),
99			nonce: None,
100			metadata_hash: None,
101		}
102	}
103
104	/// Create builder for given `RuntimeCall`. `Extrinsic` will be unsigned.
105	pub fn new_unsigned(function: impl Into<RuntimeCall>) -> Self {
106		Self { function: function.into(), signer: None, nonce: None, metadata_hash: None }
107	}
108
109	/// Create builder for `pallet_call::bench_transfer` from given `TransferData`.
110	pub fn new_bench_call(transfer: TransferData) -> Self {
111		Self::new_unsigned(PalletCall::bench_call { transfer })
112	}
113
114	/// Create builder for given `Transfer`. Transfer `nonce` will be used as `Extrinsic` nonce.
115	/// Transfer `from` will be used as Extrinsic signer.
116	pub fn new_transfer(transfer: Transfer) -> Self {
117		Self {
118			nonce: Some(transfer.nonce),
119			signer: Some(transfer.from.clone()),
120			metadata_hash: None,
121			..Self::new(BalancesCall::transfer_allow_death {
122				dest: transfer.to,
123				value: transfer.amount,
124			})
125		}
126	}
127
128	/// Create builder for `PalletCall::include_data` call using given parameters
129	pub fn new_include_data(data: Vec<u8>) -> Self {
130		Self::new(PalletCall::include_data { data })
131	}
132
133	/// Create builder for `PalletCall::storage_change` call using given parameters. Will
134	/// create unsigned Extrinsic.
135	pub fn new_storage_change(key: Vec<u8>, value: Option<Vec<u8>>) -> Self {
136		Self::new_unsigned(PalletCall::storage_change { key, value })
137	}
138
139	/// Create builder for `PalletCall::offchain_index_set` call using given parameters
140	pub fn new_offchain_index_set(key: Vec<u8>, value: Vec<u8>) -> Self {
141		Self::new(PalletCall::offchain_index_set { key, value })
142	}
143
144	/// Create builder for `PalletCall::offchain_index_clear` call using given parameters
145	pub fn new_offchain_index_clear(key: Vec<u8>) -> Self {
146		Self::new(PalletCall::offchain_index_clear { key })
147	}
148
149	/// Create builder for `PalletCall::indexed_call` call using given parameters
150	pub fn new_indexed_call(data: Vec<u8>) -> Self {
151		Self::new(PalletCall::indexed_call { data })
152	}
153
154	/// Create builder for `PalletCall::new_deposit_log_digest_item` call using given `log`
155	pub fn new_deposit_log_digest_item(log: sp_runtime::generic::DigestItem) -> Self {
156		Self::new_unsigned(PalletCall::deposit_log_digest_item { log })
157	}
158
159	/// Create builder for `PalletCall::Call::new_deposit_log_digest_item`
160	pub fn new_fill_block(ratio: Perbill) -> Self {
161		Self::new(PalletCall::fill_block { ratio })
162	}
163
164	/// Create builder for `PalletCall::call_do_not_propagate` call using given parameters
165	pub fn new_call_do_not_propagate() -> Self {
166		Self::new(PalletCall::call_do_not_propagate {})
167	}
168
169	/// Create builder for `PalletCall::call_with_priority` call using given parameters
170	pub fn new_call_with_priority(priority: TransactionPriority) -> Self {
171		Self::new(PalletCall::call_with_priority { priority })
172	}
173
174	/// Create builder for `PalletCall::read` call using given parameters
175	pub fn new_read(count: u32) -> Self {
176		Self::new_unsigned(PalletCall::read { count })
177	}
178
179	/// Create builder for `PalletCall::read` call using given parameters
180	pub fn new_read_and_panic(count: u32) -> Self {
181		Self::new_unsigned(PalletCall::read_and_panic { count })
182	}
183
184	/// Unsigned `Extrinsic` will be created
185	pub fn unsigned(mut self) -> Self {
186		self.signer = None;
187		self
188	}
189
190	/// Given `nonce` will be set in `Extrinsic`
191	pub fn nonce(mut self, nonce: Nonce) -> Self {
192		self.nonce = Some(nonce);
193		self
194	}
195
196	/// Extrinsic will be signed by `signer`
197	pub fn signer(mut self, signer: Pair) -> Self {
198		self.signer = Some(signer);
199		self
200	}
201
202	/// Metadata hash to put into the signed data of the extrinsic.
203	pub fn metadata_hash(mut self, metadata_hash: [u8; 32]) -> Self {
204		self.metadata_hash = Some(metadata_hash);
205		self
206	}
207
208	/// Build `Extrinsic` using embedded parameters
209	pub fn build(self) -> Extrinsic {
210		if let Some(signer) = self.signer {
211			let tx_ext = (
212				(CheckNonce::from(self.nonce.unwrap_or(0)), CheckWeight::new()),
213				CheckSubstrateCall {},
214				self.metadata_hash
215					.map(CheckMetadataHash::new_with_custom_hash)
216					.unwrap_or_else(|| CheckMetadataHash::new(false)),
217				frame_system::WeightReclaim::new(),
218			);
219			let raw_payload = SignedPayload::from_raw(
220				self.function.clone(),
221				tx_ext.clone(),
222				tx_ext.implicit().unwrap(),
223			);
224			let signature = raw_payload.using_encoded(|e| signer.sign(e));
225
226			Extrinsic::new_signed(self.function, signer.public(), signature, tx_ext)
227		} else {
228			Extrinsic::new_bare(self.function)
229		}
230	}
231}