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			} => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }),
74			Extrinsic {
75				function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }),
76				preamble: Preamble::Bare(_),
77			} => Ok(transfer.clone()),
78			_ => Err(()),
79		}
80	}
81}
82
83/// Generates `Extrinsic`
84pub struct ExtrinsicBuilder {
85	function: RuntimeCall,
86	signer: Option<Pair>,
87	nonce: Option<Nonce>,
88	metadata_hash: Option<[u8; 32]>,
89}
90
91impl ExtrinsicBuilder {
92	/// Create builder for given `RuntimeCall`. By default `Extrinsic` will be signed by `Alice`.
93	pub fn new(function: impl Into<RuntimeCall>) -> Self {
94		Self {
95			function: function.into(),
96			signer: Some(Sr25519Keyring::Alice.pair()),
97			nonce: None,
98			metadata_hash: None,
99		}
100	}
101
102	/// Create builder for given `RuntimeCall`. `Extrinsic` will be unsigned.
103	pub fn new_unsigned(function: impl Into<RuntimeCall>) -> Self {
104		Self { function: function.into(), signer: None, nonce: None, metadata_hash: None }
105	}
106
107	/// Create builder for `pallet_call::bench_transfer` from given `TransferData`.
108	pub fn new_bench_call(transfer: TransferData) -> Self {
109		Self::new_unsigned(PalletCall::bench_call { transfer })
110	}
111
112	/// Create builder for given `Transfer`. Transfer `nonce` will be used as `Extrinsic` nonce.
113	/// Transfer `from` will be used as Extrinsic signer.
114	pub fn new_transfer(transfer: Transfer) -> Self {
115		Self {
116			nonce: Some(transfer.nonce),
117			signer: Some(transfer.from.clone()),
118			metadata_hash: None,
119			..Self::new(BalancesCall::transfer_allow_death {
120				dest: transfer.to,
121				value: transfer.amount,
122			})
123		}
124	}
125
126	/// Create builder for `PalletCall::include_data` call using given parameters
127	pub fn new_include_data(data: Vec<u8>) -> Self {
128		Self::new(PalletCall::include_data { data })
129	}
130
131	/// Create builder for `PalletCall::storage_change` call using given parameters. Will
132	/// create unsigned Extrinsic.
133	pub fn new_storage_change(key: Vec<u8>, value: Option<Vec<u8>>) -> Self {
134		Self::new_unsigned(PalletCall::storage_change { key, value })
135	}
136
137	/// Create builder for `PalletCall::offchain_index_set` call using given parameters
138	pub fn new_offchain_index_set(key: Vec<u8>, value: Vec<u8>) -> Self {
139		Self::new(PalletCall::offchain_index_set { key, value })
140	}
141
142	/// Create builder for `PalletCall::offchain_index_clear` call using given parameters
143	pub fn new_offchain_index_clear(key: Vec<u8>) -> Self {
144		Self::new(PalletCall::offchain_index_clear { key })
145	}
146
147	/// Create builder for `PalletCall::indexed_call` call using given parameters
148	pub fn new_indexed_call(data: Vec<u8>) -> Self {
149		Self::new(PalletCall::indexed_call { data })
150	}
151
152	/// Create builder for `PalletCall::new_deposit_log_digest_item` call using given `log`
153	pub fn new_deposit_log_digest_item(log: sp_runtime::generic::DigestItem) -> Self {
154		Self::new_unsigned(PalletCall::deposit_log_digest_item { log })
155	}
156
157	/// Create builder for `PalletCall::Call::new_deposit_log_digest_item`
158	pub fn new_fill_block(ratio: Perbill) -> Self {
159		Self::new(PalletCall::fill_block { ratio })
160	}
161
162	/// Create builder for `PalletCall::call_do_not_propagate` call using given parameters
163	pub fn new_call_do_not_propagate() -> Self {
164		Self::new(PalletCall::call_do_not_propagate {})
165	}
166
167	/// Create builder for `PalletCall::call_with_priority` call using given parameters
168	pub fn new_call_with_priority(priority: TransactionPriority) -> Self {
169		Self::new(PalletCall::call_with_priority { priority })
170	}
171
172	/// Create builder for `PalletCall::read` call using given parameters
173	pub fn new_read(count: u32) -> Self {
174		Self::new_unsigned(PalletCall::read { count })
175	}
176
177	/// Create builder for `PalletCall::read` call using given parameters
178	pub fn new_read_and_panic(count: u32) -> Self {
179		Self::new_unsigned(PalletCall::read_and_panic { count })
180	}
181
182	/// Unsigned `Extrinsic` will be created
183	pub fn unsigned(mut self) -> Self {
184		self.signer = None;
185		self
186	}
187
188	/// Given `nonce` will be set in `Extrinsic`
189	pub fn nonce(mut self, nonce: Nonce) -> Self {
190		self.nonce = Some(nonce);
191		self
192	}
193
194	/// Extrinsic will be signed by `signer`
195	pub fn signer(mut self, signer: Pair) -> Self {
196		self.signer = Some(signer);
197		self
198	}
199
200	/// Metadata hash to put into the signed data of the extrinsic.
201	pub fn metadata_hash(mut self, metadata_hash: [u8; 32]) -> Self {
202		self.metadata_hash = Some(metadata_hash);
203		self
204	}
205
206	/// Build `Extrinsic` using embedded parameters
207	pub fn build(self) -> Extrinsic {
208		if let Some(signer) = self.signer {
209			let tx_ext = (
210				(CheckNonce::from(self.nonce.unwrap_or(0)), CheckWeight::new()),
211				CheckSubstrateCall {},
212				self.metadata_hash
213					.map(CheckMetadataHash::new_with_custom_hash)
214					.unwrap_or_else(|| CheckMetadataHash::new(false)),
215				frame_system::WeightReclaim::new(),
216			);
217			let raw_payload = SignedPayload::from_raw(
218				self.function.clone(),
219				tx_ext.clone(),
220				tx_ext.implicit().unwrap(),
221			);
222			let signature = raw_payload.using_encoded(|e| signer.sign(e));
223
224			Extrinsic::new_signed(self.function, signer.public(), signature, tx_ext)
225		} else {
226			Extrinsic::new_bare(self.function)
227		}
228	}
229}