Skip to main content

frame_decode/methods/
extrinsic_encoder.rs

1// Copyright (C) 2022-2026 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the frame-decode crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod transaction_extension;
17mod transaction_extensions;
18use super::extrinsic_type_info::{
19    ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicExtensionInfoArg, ExtrinsicInfoError,
20    ExtrinsicSignatureInfo, ExtrinsicTypeInfo,
21};
22use alloc::vec::Vec;
23use parity_scale_codec::Encode;
24use scale_encode::{EncodeAsFields, EncodeAsType};
25use scale_type_resolver::{Field, TypeResolver};
26
27pub use transaction_extension::{TransactionExtension, TransactionExtensionError};
28pub use transaction_extensions::{TransactionExtensions, TransactionExtensionsError};
29
30/// An error returned trying to encode extrinsic call data.
31#[non_exhaustive]
32#[allow(missing_docs)]
33#[derive(Debug, thiserror::Error)]
34pub enum ExtrinsicEncodeError {
35    #[error("Cannot get extrinsic info: {0}")]
36    CannotGetInfo(ExtrinsicInfoError<'static>),
37    #[error("Extrinsic encoding failed: cannot encode call data: {0}")]
38    CannotEncodeCallData(scale_encode::Error),
39    #[error("Extrinsic encoding failed: cannot encode address: {0}")]
40    CannotEncodeAddress(scale_encode::Error),
41    #[error("Extrinsic encoding failed: cannot encode signature: {0}")]
42    CannotEncodeSignature(scale_encode::Error),
43    #[error("Extrinsic encoding failed: cannot encode transaction extensions: {0}")]
44    TransactionExtensions(TransactionExtensionsError),
45    #[error(
46        "Extrinsic encoding failed: cannot find a transaction extensions version which relies only on the transaction extensions we were given."
47    )]
48    CannotFindGoodExtensionVersion,
49}
50
51/// Encode a V4 unsigned extrinsic (also known as an inherent).
52///
53/// This is the same as [`encode_v4_unsigned_to`], but returns the encoded extrinsic as a `Vec<u8>`,
54/// rather than accepting a mutable output buffer.
55///
56/// # Example
57///
58/// ```rust
59/// use frame_decode::extrinsics::encode_v4_unsigned;
60/// use frame_metadata::RuntimeMetadata;
61/// use parity_scale_codec::Decode;
62///
63/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
64/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
65///
66/// // Encode a call to Timestamp.set with an argument.
67/// // The call_data type must implement `scale_encode::EncodeAsFields`.
68/// let call_data = scale_value::value!({
69///     now: 1234567890u64,
70/// });
71///
72/// let encoded = encode_v4_unsigned(
73///     "Timestamp",
74///     "set",
75///     &call_data,
76///     &metadata,
77///     &metadata.types,
78/// ).unwrap();
79/// ```
80pub fn encode_v4_unsigned<CallData, Info, Resolver>(
81    pallet_name: &str,
82    call_name: &str,
83    call_data: &CallData,
84    info: &Info,
85    type_resolver: &Resolver,
86) -> Result<Vec<u8>, ExtrinsicEncodeError>
87where
88    CallData: EncodeAsFields,
89    Resolver: TypeResolver<TypeId = Info::TypeId>,
90    Info: ExtrinsicTypeInfo,
91{
92    let mut out = Vec::new();
93    encode_v4_unsigned_to(
94        pallet_name,
95        call_name,
96        call_data,
97        info,
98        type_resolver,
99        &mut out,
100    )?;
101    Ok(out)
102}
103
104/// Encode a V4 unsigned extrinsic (also known as an inherent) to a provided output buffer.
105///
106/// This is the same as [`encode_v4_unsigned`], but writes the encoded extrinsic to the provided
107/// `Vec<u8>` rather than returning a new one.
108///
109/// # Example
110///
111/// ```rust
112/// use frame_decode::extrinsics::encode_v4_unsigned_to;
113/// use frame_metadata::RuntimeMetadata;
114/// use parity_scale_codec::Decode;
115///
116/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
117/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
118///
119/// // Encode a call to Timestamp.set with an argument.
120/// let call_data = scale_value::value!({
121///     now: 1234567890u64,
122/// });
123///
124/// let mut encoded = Vec::new();
125/// encode_v4_unsigned_to(
126///     "Timestamp",
127///     "set",
128///     &call_data,
129///     &metadata,
130///     &metadata.types,
131///     &mut encoded,
132/// ).unwrap();
133/// ```
134pub fn encode_v4_unsigned_to<CallData, Info, Resolver>(
135    pallet_name: &str,
136    call_name: &str,
137    call_data: &CallData,
138    info: &Info,
139    type_resolver: &Resolver,
140    out: &mut Vec<u8>,
141) -> Result<(), ExtrinsicEncodeError>
142where
143    CallData: EncodeAsFields,
144    Resolver: TypeResolver<TypeId = Info::TypeId>,
145    Info: ExtrinsicTypeInfo,
146{
147    let call_info = info
148        .extrinsic_call_info_by_name(pallet_name, call_name)
149        .map_err(|i| i.into_owned())
150        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
151
152    encode_v4_unsigned_with_info_to(call_data, type_resolver, &call_info, out)
153}
154
155/// Encode a V4 unsigned extrinsic (also known as an inherent) to a provided output buffer,
156/// using pre-computed call information.
157///
158/// Unlike [`encode_v4_unsigned_to`], which obtains the call info internally given the pallet and call names,
159/// this function takes the call info as an argument. This is useful if you already have the call info available,
160/// for example if you are encoding multiple extrinsics for the same call.
161pub fn encode_v4_unsigned_with_info_to<CallData, Resolver>(
162    call_data: &CallData,
163    type_resolver: &Resolver,
164    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
165    out: &mut Vec<u8>,
166) -> Result<(), ExtrinsicEncodeError>
167where
168    CallData: EncodeAsFields,
169    Resolver: TypeResolver,
170{
171    encode_unsigned_at_version_with_info_to(
172        call_data,
173        call_info,
174        type_resolver,
175        TransactionVersion::V4,
176        out,
177    )
178}
179
180/// Encode a V4 signed extrinsic, ready to submit.
181///
182/// A signed V4 extrinsic includes an address, signature, and transaction extensions (such as
183/// nonce and tip) alongside the call data. The signature should be computed over the signer
184/// payload, which can be obtained via [`encode_v4_signer_payload`].
185///
186/// This is the same as [`encode_v4_signed_to`], but returns the encoded extrinsic as a `Vec<u8>`,
187/// rather than accepting a mutable output buffer.
188///
189/// # Example
190///
191/// ```rust,ignore
192/// use frame_decode::extrinsics::{encode_v4_signed, TransactionExtensions};
193/// use frame_metadata::RuntimeMetadata;
194/// use parity_scale_codec::Decode;
195///
196/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
197/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
198///
199/// // The call data, address, signature, and transaction extensions must implement
200/// // the appropriate scale_encode traits.
201/// let call_data = /* ... */;
202/// let address = /* your address type */;
203/// let signature = /* your signature type */;
204/// let transaction_extensions = /* your TransactionExtensions impl */;
205///
206/// let encoded = encode_v4_signed(
207///     "Balances",
208///     "transfer_keep_alive",
209///     &call_data,
210///     &transaction_extensions,
211///     &address,
212///     &signature,
213///     &metadata,
214///     &metadata.types,
215/// ).unwrap();
216/// ```
217#[allow(clippy::too_many_arguments)]
218pub fn encode_v4_signed<CallData, Info, Resolver, Exts, Address, Signature>(
219    pallet_name: &str,
220    call_name: &str,
221    call_data: &CallData,
222    transaction_extensions: &Exts,
223    address: &Address,
224    signature: &Signature,
225    info: &Info,
226    type_resolver: &Resolver,
227) -> Result<Vec<u8>, ExtrinsicEncodeError>
228where
229    CallData: EncodeAsFields,
230    Resolver: TypeResolver<TypeId = Info::TypeId>,
231    Info: ExtrinsicTypeInfo,
232    Exts: TransactionExtensions<Resolver>,
233    Address: EncodeAsType,
234    Signature: EncodeAsType,
235{
236    let mut out = Vec::new();
237    encode_v4_signed_to(
238        pallet_name,
239        call_name,
240        call_data,
241        transaction_extensions,
242        address,
243        signature,
244        info,
245        type_resolver,
246        &mut out,
247    )?;
248    Ok(out)
249}
250
251/// Encode a V4 signed extrinsic to a provided output buffer.
252///
253/// A signed extrinsic includes an address, signature, and transaction extensions (such as
254/// nonce and tip) alongside the call data. The signature should be computed over the signer
255/// payload, which can be obtained via [`encode_v4_signer_payload`].
256///
257/// This is the same as [`encode_v4_signed`], but writes the encoded extrinsic to the provided
258/// `Vec<u8>` rather than returning a new one.
259///
260/// # Example
261///
262/// ```rust,ignore
263/// use frame_decode::extrinsics::{encode_v4_signed_to, TransactionExtensions};
264/// use frame_metadata::RuntimeMetadata;
265/// use parity_scale_codec::Decode;
266///
267/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
268/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
269///
270/// let call_data = /* ... */;
271/// let address = /* your address type */;
272/// let signature = /* your signature type */;
273/// let transaction_extensions = /* your TransactionExtensions impl */;
274///
275/// let mut encoded = Vec::new();
276/// encode_v4_signed_to(
277///     "Balances",
278///     "transfer_keep_alive",
279///     &call_data,
280///     &transaction_extensions,
281///     &address,
282///     &signature,
283///     &metadata,
284///     &metadata.types,
285///     &mut encoded,
286/// ).unwrap();
287/// ```
288#[allow(clippy::too_many_arguments)]
289pub fn encode_v4_signed_to<CallData, Info, Resolver, Exts, Address, Signature>(
290    pallet_name: &str,
291    call_name: &str,
292    call_data: &CallData,
293    transaction_extensions: &Exts,
294    address: &Address,
295    signature: &Signature,
296    info: &Info,
297    type_resolver: &Resolver,
298    out: &mut Vec<u8>,
299) -> Result<(), ExtrinsicEncodeError>
300where
301    CallData: EncodeAsFields,
302    Resolver: TypeResolver<TypeId = Info::TypeId>,
303    Info: ExtrinsicTypeInfo,
304    Exts: TransactionExtensions<Resolver>,
305    Address: EncodeAsType,
306    Signature: EncodeAsType,
307{
308    let call_info = info
309        .extrinsic_call_info_by_name(pallet_name, call_name)
310        .map_err(|i| i.into_owned())
311        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
312
313    let ext_info = info
314        .extrinsic_extension_info(None)
315        .map_err(|i| i.into_owned())
316        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
317
318    let sig_info = info
319        .extrinsic_signature_info()
320        .map_err(|i| i.into_owned())
321        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
322
323    encode_v4_signed_with_info_to(
324        call_data,
325        transaction_extensions,
326        address,
327        signature,
328        type_resolver,
329        &call_info,
330        &sig_info,
331        &ext_info,
332        out,
333    )
334}
335
336/// Encode a V4 signed extrinsic to a provided output buffer, using pre-computed type information.
337///
338/// Unlike [`encode_v4_signed_to`], which obtains the call, signature, and extension info internally
339/// given the pallet and call names, this function takes these as arguments. This is useful if you
340/// already have this information available, for example if you are encoding multiple extrinsics.
341#[allow(clippy::too_many_arguments)]
342pub fn encode_v4_signed_with_info_to<CallData, Resolver, Exts, Address, Signature>(
343    call_data: &CallData,
344    transaction_extensions: &Exts,
345    address: &Address,
346    signature: &Signature,
347    type_resolver: &Resolver,
348    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
349    sig_info: &ExtrinsicSignatureInfo<Resolver::TypeId>,
350    ext_info: &ExtrinsicExtensionInfo<Resolver::TypeId>,
351    out: &mut Vec<u8>,
352) -> Result<(), ExtrinsicEncodeError>
353where
354    CallData: EncodeAsFields,
355    Resolver: TypeResolver,
356    Exts: TransactionExtensions<Resolver>,
357    Address: EncodeAsType,
358    Signature: EncodeAsType,
359{
360    // Encode the "inner" bytes
361    let mut encoded_inner = Vec::new();
362
363    // "is signed" + transaction protocol version (4)
364    (0b10000000 + 4u8).encode_to(&mut encoded_inner);
365
366    // Who is this transaction from (corresponds to public key of signature)
367    address
368        .encode_as_type_to(
369            sig_info.address_id.clone(),
370            type_resolver,
371            &mut encoded_inner,
372        )
373        .map_err(ExtrinsicEncodeError::CannotEncodeAddress)?;
374
375    // Signature for the above identity
376    signature
377        .encode_as_type_to(
378            sig_info.signature_id.clone(),
379            type_resolver,
380            &mut encoded_inner,
381        )
382        .map_err(ExtrinsicEncodeError::CannotEncodeSignature)?;
383
384    // Signed extensions (now Transaction Extensions)
385    encode_transaction_extension_values(
386        &ext_info.extension_ids,
387        transaction_extensions,
388        type_resolver,
389        &mut encoded_inner,
390    )
391    .map_err(ExtrinsicEncodeError::TransactionExtensions)?;
392
393    // And now the actual call data, ie the arguments we're passing to the call
394    encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut encoded_inner)?;
395
396    // Now, encoding these inner bytes prefixes the compact length to the beginning:
397    encoded_inner.encode_to(out);
398    Ok(())
399}
400
401/// Encode the signer payload for a V4 signed extrinsic.
402///
403/// The signer payload is the data that should be signed to produce the signature for
404/// a signed extrinsic. It consists of the encoded call data, the transaction extension
405/// values, and the transaction extension implicit data. If the resulting payload exceeds
406/// 256 bytes, it is hashed using Blake2-256.
407///
408/// Use this function to obtain the bytes that should be signed, then pass the resulting
409/// signature to [`encode_v4_signed`] to construct the final extrinsic.
410///
411/// # Example
412///
413/// ```rust,ignore
414/// use frame_decode::extrinsics::{encode_v4_signer_payload, TransactionExtensions};
415/// use frame_metadata::RuntimeMetadata;
416/// use parity_scale_codec::Decode;
417///
418/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
419/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
420///
421/// let call_data = /* ... */;
422/// let transaction_extensions = /* your TransactionExtensions impl */;
423///
424/// // Get the payload to sign
425/// let signer_payload = encode_v4_signer_payload(
426///     "Balances",
427///     "transfer_keep_alive",
428///     &call_data,
429///     &transaction_extensions,
430///     &metadata,
431///     &metadata.types,
432/// ).unwrap();
433///
434/// // Sign the payload with your signing key, then use encode_v4_signed
435/// // to construct the final extrinsic.
436/// ```
437pub fn encode_v4_signer_payload<CallData, Info, Resolver, Exts>(
438    pallet_name: &str,
439    call_name: &str,
440    call_data: &CallData,
441    transaction_extensions: &Exts,
442    info: &Info,
443    type_resolver: &Resolver,
444) -> Result<Vec<u8>, ExtrinsicEncodeError>
445where
446    CallData: EncodeAsFields,
447    Resolver: TypeResolver<TypeId = Info::TypeId>,
448    Info: ExtrinsicTypeInfo,
449    Exts: TransactionExtensions<Resolver>,
450    Info::TypeId: Clone,
451{
452    let call_info = info
453        .extrinsic_call_info_by_name(pallet_name, call_name)
454        .map_err(|i| i.into_owned())
455        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
456
457    let ext_info = info
458        .extrinsic_extension_info(None)
459        .map_err(|i| i.into_owned())
460        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
461
462    encode_v4_signer_payload_with_info(
463        call_data,
464        transaction_extensions,
465        type_resolver,
466        &call_info,
467        &ext_info,
468    )
469}
470
471/// Encode the signer payload for a V4 signed extrinsic, using pre-computed type information.
472///
473/// Unlike [`encode_v4_signer_payload`], which obtains the call and extension info internally
474/// given the pallet and call names, this function takes these as arguments. This is useful if you
475/// already have this information available.
476///
477/// The signer payload consists of the encoded call data, the transaction extension values,
478/// and the transaction extension implicit data. If the resulting payload exceeds 256 bytes,
479/// it is hashed using Blake2-256.
480pub fn encode_v4_signer_payload_with_info<CallData, Resolver, Exts>(
481    call_data: &CallData,
482    transaction_extensions: &Exts,
483    type_resolver: &Resolver,
484    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
485    ext_info: &ExtrinsicExtensionInfo<Resolver::TypeId>,
486) -> Result<Vec<u8>, ExtrinsicEncodeError>
487where
488    CallData: EncodeAsFields,
489    Resolver: TypeResolver,
490    Exts: TransactionExtensions<Resolver>,
491{
492    let mut out = Vec::new();
493
494    // First encode call data
495    encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut out)?;
496
497    // Then the signer payload value (ie roughly the bytes that will appear in the tx)
498    encode_transaction_extension_values(
499        &ext_info.extension_ids,
500        transaction_extensions,
501        type_resolver,
502        &mut out,
503    )
504    .map_err(ExtrinsicEncodeError::TransactionExtensions)?;
505
506    // Then the signer payload implicits (ie data we want to verify that is NOT in the tx)
507    encode_transaction_extension_implicits(
508        &ext_info.extension_ids,
509        transaction_extensions,
510        type_resolver,
511        &mut out,
512    )
513    .map_err(ExtrinsicEncodeError::TransactionExtensions)?;
514
515    // Finally we need to hash it if it's too long
516    if out.len() > 256 {
517        out = sp_crypto_hashing::blake2_256(&out).to_vec();
518    }
519
520    Ok(out)
521}
522
523/// Encode a V5 bare extrinsic (also known as an inherent), ready to submit.
524///
525/// V5 bare extrinsics contain only call data with no transaction extensions or signature.
526/// They are functionally equivalent to V4 unsigned extrinsics and are typically used for
527/// inherents (data provided by block authors).
528///
529/// This is the same as [`encode_v5_bare_to`], but returns the encoded extrinsic as a `Vec<u8>`,
530/// rather than accepting a mutable output buffer.
531///
532/// # Example
533///
534/// ```rust
535/// use frame_decode::extrinsics::encode_v5_bare;
536/// use frame_metadata::RuntimeMetadata;
537/// use parity_scale_codec::Decode;
538///
539/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
540/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
541///
542/// // Encode a call to Timestamp.set with an argument.
543/// let call_data = scale_value::value!({
544///     now: 1234567890u64,
545/// });
546///
547/// let encoded = encode_v5_bare(
548///     "Timestamp",
549///     "set",
550///     &call_data,
551///     &metadata,
552///     &metadata.types,
553/// ).unwrap();
554/// ```
555pub fn encode_v5_bare<CallData, Info, Resolver>(
556    pallet_name: &str,
557    call_name: &str,
558    call_data: &CallData,
559    info: &Info,
560    type_resolver: &Resolver,
561) -> Result<Vec<u8>, ExtrinsicEncodeError>
562where
563    CallData: EncodeAsFields,
564    Resolver: TypeResolver<TypeId = Info::TypeId>,
565    Info: ExtrinsicTypeInfo,
566{
567    let mut out = Vec::new();
568    encode_v5_bare_to(
569        pallet_name,
570        call_name,
571        call_data,
572        info,
573        type_resolver,
574        &mut out,
575    )?;
576    Ok(out)
577}
578
579/// Encode a V5 bare extrinsic (also known as an inherent) to a provided output buffer.
580///
581/// This is the same as [`encode_v5_bare`], but writes the encoded extrinsic to the provided
582/// `Vec<u8>` rather than returning a new one.
583///
584/// # Example
585///
586/// ```rust
587/// use frame_decode::extrinsics::encode_v5_bare_to;
588/// use frame_metadata::RuntimeMetadata;
589/// use parity_scale_codec::Decode;
590///
591/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
592/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
593///
594/// // Encode a call to Timestamp.set with an argument.
595/// let call_data = scale_value::value!({
596///     now: 1234567890u64,
597/// });
598///
599/// let mut encoded = Vec::new();
600/// encode_v5_bare_to(
601///     "Timestamp",
602///     "set",
603///     &call_data,
604///     &metadata,
605///     &metadata.types,
606///     &mut encoded,
607/// ).unwrap();
608/// ```
609pub fn encode_v5_bare_to<CallData, Info, Resolver>(
610    pallet_name: &str,
611    call_name: &str,
612    call_data: &CallData,
613    info: &Info,
614    type_resolver: &Resolver,
615    out: &mut Vec<u8>,
616) -> Result<(), ExtrinsicEncodeError>
617where
618    CallData: EncodeAsFields,
619    Resolver: TypeResolver<TypeId = Info::TypeId>,
620    Info: ExtrinsicTypeInfo,
621{
622    let call_info = info
623        .extrinsic_call_info_by_name(pallet_name, call_name)
624        .map_err(|i| i.into_owned())
625        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
626
627    encode_v5_bare_with_info_to(call_data, type_resolver, &call_info, out)
628}
629
630/// Encode a V5 bare extrinsic (also known as an inherent) to a provided output buffer,
631/// using pre-computed call information.
632///
633/// Unlike [`encode_v5_bare_to`], which obtains the call info internally given the pallet and call names,
634/// this function takes the call info as an argument. This is useful if you already have the call info available,
635/// for example if you are encoding multiple extrinsics for the same call.
636pub fn encode_v5_bare_with_info_to<CallData, Resolver>(
637    call_data: &CallData,
638    type_resolver: &Resolver,
639    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
640    out: &mut Vec<u8>,
641) -> Result<(), ExtrinsicEncodeError>
642where
643    CallData: EncodeAsFields,
644    Resolver: TypeResolver,
645{
646    encode_unsigned_at_version_with_info_to(
647        call_data,
648        call_info,
649        type_resolver,
650        TransactionVersion::V5,
651        out,
652    )
653}
654
655/// Determine the best transaction extension version to use for a V5 general extrinsic.
656///
657/// V5 general extrinsics support multiple versions of transaction extensions. This function
658/// iterates through the available extension versions and returns the first version for which
659/// all required extension data is provided.
660///
661/// Use this function to determine which `transaction_extension_version` to pass to
662/// [`encode_v5_general`] or [`encode_v5_general_to`].
663///
664/// # Errors
665///
666/// Returns [`ExtrinsicEncodeError::CannotFindGoodExtensionVersion`] if no extension version
667/// can be found for which all required data is available.
668///
669/// # Example
670///
671/// ```rust,ignore
672/// use frame_decode::extrinsics::{best_v5_general_transaction_extension_version, encode_v5_general};
673/// use frame_metadata::RuntimeMetadata;
674/// use parity_scale_codec::Decode;
675///
676/// let metadata_bytes = std::fs::read("artifacts/metadata.scale").unwrap();
677/// let RuntimeMetadata::V16(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
678///
679/// let transaction_extensions = /* your TransactionExtensions impl */;
680///
681/// // Find the best extension version for your provided extensions
682/// let ext_version = best_v5_general_transaction_extension_version(
683///     &transaction_extensions,
684///     &metadata,
685/// ).unwrap();
686///
687/// // Use this version when encoding the extrinsic
688/// let encoded = encode_v5_general(
689///     "Balances",
690///     "transfer_keep_alive",
691///     &call_data,
692///     ext_version,
693///     &transaction_extensions,
694///     &metadata,
695///     &metadata.types,
696/// ).unwrap();
697/// ```
698pub fn best_v5_general_transaction_extension_version<Exts, Info, Resolver>(
699    transaction_extensions: &Exts,
700    info: &Info,
701    type_resolver: &Resolver,
702) -> Result<u8, ExtrinsicEncodeError>
703where
704    Info: ExtrinsicTypeInfo,
705    Exts: TransactionExtensions<Resolver>,
706    Resolver: TypeResolver<TypeId = Info::TypeId>,
707    Info::TypeId: Clone,
708{
709    let extension_versions = info
710        .extrinsic_extension_version_info()
711        .map_err(|i| i.into_owned())
712        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
713
714    for ext_version in extension_versions {
715        // get extension info for each version.
716        let ext_info = info
717            .extrinsic_extension_info(Some(ext_version))
718            .map_err(|i| i.into_owned())
719            .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
720
721        // Do we have all of the extension data for this version?
722        let have_data = ext_info.extension_ids.iter().all(|e| {
723            let is_value_empty = is_type_empty(e.id.clone(), type_resolver);
724            let is_value_option = is_type_option(e.id.clone(), type_resolver);
725            let is_implicit_empty = is_type_empty(e.implicit_id.clone(), type_resolver);
726            ((is_value_empty || is_value_option) && is_implicit_empty)
727                || transaction_extensions.contains_extension(&e.name)
728        });
729
730        // If we have all of the data we need, encode the extrinsic,
731        // else loop and try the next extension version.
732        if have_data {
733            return Ok(ext_version);
734        }
735    }
736
737    Err(ExtrinsicEncodeError::CannotFindGoodExtensionVersion)
738}
739
740/// Encode a V5 general extrinsic, ready to submit.
741///
742/// V5 general extrinsics include transaction extensions but no separate signature field.
743/// Instead, the signature (if needed) is provided as part of one of the transaction extensions.
744/// This is the new extrinsic format introduced in newer Substrate runtimes.
745///
746/// Use [`best_v5_general_transaction_extension_version`] to determine which extension version
747/// to use based on the extensions you have available.
748///
749/// This is the same as [`encode_v5_general_to`], but returns the encoded extrinsic as a `Vec<u8>`,
750/// rather than accepting a mutable output buffer.
751///
752/// # Example
753///
754/// ```rust,ignore
755/// use frame_decode::extrinsics::{encode_v5_general, best_v5_general_transaction_extension_version};
756/// use frame_metadata::RuntimeMetadata;
757/// use parity_scale_codec::Decode;
758///
759/// let metadata_bytes = std::fs::read("artifacts/metadata.scale").unwrap();
760/// let RuntimeMetadata::V16(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
761///
762/// let call_data = /* ... */;
763/// let transaction_extensions = /* your TransactionExtensions impl */;
764///
765/// let ext_version = best_v5_general_transaction_extension_version(
766///     &transaction_extensions,
767///     &metadata,
768/// ).unwrap();
769///
770/// let encoded = encode_v5_general(
771///     "Balances",
772///     "transfer_keep_alive",
773///     &call_data,
774///     ext_version,
775///     &transaction_extensions,
776///     &metadata,
777///     &metadata.types,
778/// ).unwrap();
779/// ```
780pub fn encode_v5_general<CallData, Info, Resolver, Exts>(
781    pallet_name: &str,
782    call_name: &str,
783    call_data: &CallData,
784    transaction_extension_version: u8,
785    transaction_extensions: &Exts,
786    info: &Info,
787    type_resolver: &Resolver,
788) -> Result<Vec<u8>, ExtrinsicEncodeError>
789where
790    CallData: EncodeAsFields,
791    Resolver: TypeResolver<TypeId = Info::TypeId>,
792    Info: ExtrinsicTypeInfo,
793    Exts: TransactionExtensions<Resolver>,
794{
795    let mut out = Vec::new();
796    encode_v5_general_to(
797        pallet_name,
798        call_name,
799        call_data,
800        transaction_extension_version,
801        transaction_extensions,
802        info,
803        type_resolver,
804        &mut out,
805    )?;
806    Ok(out)
807}
808
809/// Encode a V5 general extrinsic to a provided output buffer.
810///
811/// V5 general extrinsics include transaction extensions but no separate signature field.
812/// Instead, the signature (if needed) is provided as part of one of the transaction extensions.
813///
814/// This is the same as [`encode_v5_general`], but writes the encoded extrinsic to the provided
815/// `Vec<u8>` rather than returning a new one.
816///
817/// # Example
818///
819/// ```rust,ignore
820/// use frame_decode::extrinsics::{encode_v5_general_to, best_v5_general_transaction_extension_version};
821/// use frame_metadata::RuntimeMetadata;
822/// use parity_scale_codec::Decode;
823///
824/// let metadata_bytes = std::fs::read("artifacts/metadata.scale").unwrap();
825/// let RuntimeMetadata::V16(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
826///
827/// let call_data = /* ... */;
828/// let transaction_extensions = /* your TransactionExtensions impl */;
829///
830/// let ext_version = best_v5_general_transaction_extension_version(
831///     &transaction_extensions,
832///     &metadata,
833/// ).unwrap();
834///
835/// let mut encoded = Vec::new();
836/// encode_v5_general_to(
837///     "Balances",
838///     "transfer_keep_alive",
839///     &call_data,
840///     ext_version,
841///     &transaction_extensions,
842///     &metadata,
843///     &metadata.types,
844///     &mut encoded,
845/// ).unwrap();
846/// ```
847#[allow(clippy::too_many_arguments)]
848pub fn encode_v5_general_to<CallData, Info, Resolver, Exts>(
849    pallet_name: &str,
850    call_name: &str,
851    call_data: &CallData,
852    transaction_extension_version: u8,
853    transaction_extensions: &Exts,
854    info: &Info,
855    type_resolver: &Resolver,
856    out: &mut Vec<u8>,
857) -> Result<(), ExtrinsicEncodeError>
858where
859    CallData: EncodeAsFields,
860    Resolver: TypeResolver<TypeId = Info::TypeId>,
861    Info: ExtrinsicTypeInfo,
862    Exts: TransactionExtensions<Resolver>,
863{
864    let call_info = info
865        .extrinsic_call_info_by_name(pallet_name, call_name)
866        .map_err(|i| i.into_owned())
867        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
868
869    let ext_info = info
870        .extrinsic_extension_info(Some(transaction_extension_version))
871        .map_err(|i| i.into_owned())
872        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
873
874    encode_v5_general_with_info_to(
875        call_data,
876        transaction_extension_version,
877        transaction_extensions,
878        type_resolver,
879        &call_info,
880        &ext_info,
881        out,
882    )
883}
884
885/// Encode a V5 general extrinsic to a provided output buffer, using pre-computed type information.
886///
887/// Unlike [`encode_v5_general_to`], which obtains the call and extension info internally
888/// given the pallet and call names, this function takes these as arguments. This is useful if you
889/// already have this information available, for example if you are encoding multiple extrinsics.
890pub fn encode_v5_general_with_info_to<CallData, Resolver, Exts>(
891    call_data: &CallData,
892    transaction_extension_version: u8,
893    transaction_extensions: &Exts,
894    type_resolver: &Resolver,
895    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
896    ext_info: &ExtrinsicExtensionInfo<Resolver::TypeId>,
897    out: &mut Vec<u8>,
898) -> Result<(), ExtrinsicEncodeError>
899where
900    CallData: EncodeAsFields,
901    Resolver: TypeResolver,
902    Exts: TransactionExtensions<Resolver>,
903{
904    // Encode the "inner" bytes
905    let mut encoded_inner = Vec::new();
906
907    // "is signed" (2 bits now) + transaction protocol version (5)
908    (0b01000000 + 5u8).encode_to(&mut encoded_inner);
909
910    // Version of the transaction extensions.
911    transaction_extension_version.encode_to(&mut encoded_inner);
912
913    // Transaction Extensions next. These may include a signature/address
914    encode_transaction_extension_values(
915        &ext_info.extension_ids,
916        transaction_extensions,
917        type_resolver,
918        &mut encoded_inner,
919    )
920    .map_err(ExtrinsicEncodeError::TransactionExtensions)?;
921
922    // And now the actual call data, ie the arguments we're passing to the call
923    encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut encoded_inner)?;
924
925    // Now, encoding these inner bytes prefixes the compact length to the beginning:
926    encoded_inner.encode_to(out);
927    Ok(())
928}
929
930/// Encode the signer payload for a V5 general extrinsic.
931///
932/// The signer payload is the data that should be signed to produce the signature for
933/// a general extrinsic. It consists of the transaction extension version and encoded call data,
934/// followed by the transaction extension values and implicit data of the extensions after the
935/// last authorization extension (see [`TransactionExtension::is_authorization_extension`]);
936/// an authorization extension signs only the implications which follow it.
937///
938/// Unlike [`encode_v4_signer_payload`], which conditionally hashes the payload if it exceeds
939/// 256 bytes, V5 signer payloads are always hashed using Blake2-256, returning a fixed 32-byte
940/// array.
941///
942/// Use this function to obtain the bytes that should be signed, then include the resulting
943/// signature in the appropriate transaction extension when calling [`encode_v5_general`].
944///
945/// # Example
946///
947/// ```rust,ignore
948/// use frame_decode::extrinsics::{encode_v5_signer_payload, best_v5_general_transaction_extension_version};
949/// use frame_metadata::RuntimeMetadata;
950/// use parity_scale_codec::Decode;
951///
952/// let metadata_bytes = std::fs::read("artifacts/metadata.scale").unwrap();
953/// let RuntimeMetadata::V16(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
954///
955/// let call_data = /* ... */;
956/// let transaction_extensions = /* your TransactionExtensions impl */;
957///
958/// let ext_version = best_v5_general_transaction_extension_version(
959///     &transaction_extensions,
960///     &metadata,
961/// ).unwrap();
962///
963/// // Get the 32-byte payload hash to sign
964/// let signer_payload = encode_v5_signer_payload(
965///     "Balances",
966///     "transfer_keep_alive",
967///     &call_data,
968///     ext_version,
969///     &transaction_extensions,
970///     &metadata,
971///     &metadata.types,
972/// ).unwrap();
973///
974/// // Sign the payload with your signing key, then include the signature
975/// // in your transaction extensions when calling encode_v5_general.
976/// ```
977pub fn encode_v5_signer_payload<CallData, Info, Resolver, Exts>(
978    pallet_name: &str,
979    call_name: &str,
980    call_data: &CallData,
981    transaction_extension_version: u8,
982    transaction_extensions: &Exts,
983    info: &Info,
984    type_resolver: &Resolver,
985) -> Result<[u8; 32], ExtrinsicEncodeError>
986where
987    CallData: EncodeAsFields,
988    Resolver: TypeResolver<TypeId = Info::TypeId>,
989    Info: ExtrinsicTypeInfo,
990    Exts: TransactionExtensions<Resolver>,
991    Info::TypeId: Clone,
992{
993    let call_info = info
994        .extrinsic_call_info_by_name(pallet_name, call_name)
995        .map_err(|i| i.into_owned())
996        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
997
998    let ext_info = info
999        .extrinsic_extension_info(Some(transaction_extension_version))
1000        .map_err(|i| i.into_owned())
1001        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
1002
1003    encode_v5_signer_payload_with_info(
1004        call_data,
1005        transaction_extension_version,
1006        transaction_extensions,
1007        type_resolver,
1008        &call_info,
1009        &ext_info,
1010    )
1011}
1012
1013/// Encode the signer payload for a V5 general extrinsic, using pre-computed type information.
1014///
1015/// Unlike [`encode_v5_signer_payload`], which obtains the call and extension info internally
1016/// given the pallet and call names, this function takes these as arguments. This is useful if you
1017/// already have this information available.
1018///
1019/// The signer payload consists of the transaction extension version and encoded call data,
1020/// followed by the transaction extension values and implicit data of the extensions after the
1021/// last authorization extension (see [`TransactionExtension::is_authorization_extension`]).
1022/// The result is always hashed using Blake2-256, returning a fixed 32-byte array.
1023pub fn encode_v5_signer_payload_with_info<CallData, Resolver, Exts>(
1024    call_data: &CallData,
1025    transaction_extension_version: u8,
1026    transaction_extensions: &Exts,
1027    type_resolver: &Resolver,
1028    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
1029    ext_info: &ExtrinsicExtensionInfo<Resolver::TypeId>,
1030) -> Result<[u8; 32], ExtrinsicEncodeError>
1031where
1032    CallData: EncodeAsFields,
1033    Resolver: TypeResolver,
1034    Exts: TransactionExtensions<Resolver>,
1035{
1036    let mut payload = Vec::new();
1037
1038    // First, the base implication: the transaction extension version and call data.
1039    // This is always signed, regardless of any authorization extension below.
1040    transaction_extension_version.encode_to(&mut payload);
1041    encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut payload)?;
1042
1043    // An authorization extension (eg VerifyMultiSignature) signs only the implications of the
1044    // extensions following it, so it and every extension before it contribute no bytes to the
1045    // signer payload.
1046    let signed_extension_ids = ext_info
1047        .extension_ids
1048        .iter()
1049        .rposition(|e| transaction_extensions.is_authorization_extension(&e.name))
1050        .map(|last_auth| &ext_info.extension_ids[last_auth + 1..])
1051        .unwrap_or(&ext_info.extension_ids);
1052
1053    // Then the signer payload values (ie roughly the bytes that will appear in the tx)
1054    encode_transaction_extension_values(
1055        signed_extension_ids,
1056        transaction_extensions,
1057        type_resolver,
1058        &mut payload,
1059    )
1060    .map_err(ExtrinsicEncodeError::TransactionExtensions)?;
1061
1062    // Then the signer payload implicits (ie data we want to verify that is NOT in the tx)
1063    encode_transaction_extension_implicits(
1064        signed_extension_ids,
1065        transaction_extensions,
1066        type_resolver,
1067        &mut payload,
1068    )
1069    .map_err(ExtrinsicEncodeError::TransactionExtensions)?;
1070
1071    // V5 signer payloads are always hashed, regardless of length.
1072    Ok(sp_crypto_hashing::blake2_256(&payload))
1073}
1074
1075/// Encode the call data for an extrinsic.
1076///
1077/// This is basically an alias for [`scale_encode::EncodeAsFields::encode_as_fields()`].
1078pub fn encode_call_data<CallData, Info, Resolver>(
1079    pallet_name: &str,
1080    call_name: &str,
1081    call_data: &CallData,
1082    info: &Info,
1083    type_resolver: &Resolver,
1084) -> Result<Vec<u8>, ExtrinsicEncodeError>
1085where
1086    CallData: EncodeAsFields,
1087    Resolver: TypeResolver<TypeId = Info::TypeId>,
1088    Info: ExtrinsicTypeInfo,
1089{
1090    let mut out = Vec::new();
1091    encode_call_data_to(
1092        pallet_name,
1093        call_name,
1094        call_data,
1095        info,
1096        type_resolver,
1097        &mut out,
1098    )?;
1099    Ok(out)
1100}
1101
1102/// Encode the call data for an extrinsic to the given Vec.
1103///
1104/// This is basically an alias for [`scale_encode::EncodeAsFields::encode_as_fields()`], but
1105/// with a byte for the pallet index and call index prepended.
1106pub fn encode_call_data_to<CallData, Info, Resolver>(
1107    pallet_name: &str,
1108    call_name: &str,
1109    call_data: &CallData,
1110    info: &Info,
1111    type_resolver: &Resolver,
1112    out: &mut Vec<u8>,
1113) -> Result<(), ExtrinsicEncodeError>
1114where
1115    CallData: EncodeAsFields,
1116    Resolver: TypeResolver<TypeId = Info::TypeId>,
1117    Info: ExtrinsicTypeInfo,
1118{
1119    let call_info = info
1120        .extrinsic_call_info_by_name(pallet_name, call_name)
1121        .map_err(|i| i.into_owned())
1122        .map_err(ExtrinsicEncodeError::CannotGetInfo)?;
1123
1124    encode_call_data_with_info_to(call_data, &call_info, type_resolver, out)
1125}
1126
1127/// Encode the call data for an extrinsic, given some already-computed [`ExtrinsicCallInfo`].
1128///
1129/// This is basically an alias for [`scale_encode::EncodeAsFields::encode_as_fields()`], but
1130/// with a byte for the pallet index and call index prepended.
1131pub fn encode_call_data_with_info<CallData, Info, Resolver>(
1132    call_data: &CallData,
1133    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
1134    type_resolver: &Resolver,
1135) -> Result<Vec<u8>, ExtrinsicEncodeError>
1136where
1137    Resolver: TypeResolver,
1138    CallData: EncodeAsFields,
1139{
1140    let mut out = Vec::new();
1141    encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut out)?;
1142    Ok(out)
1143}
1144
1145/// Encode the call data for an extrinsic, given some already-computed [`ExtrinsicCallInfo`],
1146/// to the given Vec.
1147///
1148/// This is basically an alias for [`scale_encode::EncodeAsFields::encode_as_fields()`], but
1149/// with a byte for the pallet index and call index prepended.
1150pub fn encode_call_data_with_info_to<CallData, Resolver>(
1151    call_data: &CallData,
1152    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
1153    type_resolver: &Resolver,
1154    out: &mut Vec<u8>,
1155) -> Result<(), ExtrinsicEncodeError>
1156where
1157    Resolver: TypeResolver,
1158    CallData: EncodeAsFields,
1159{
1160    // Pallet and call index to identify the call:
1161    call_info.pallet_index.encode_to(out);
1162    call_info.call_index.encode_to(out);
1163
1164    // Arguments to this call:
1165    let mut fields = call_info.args.iter().map(|arg| Field {
1166        name: Some(&*arg.name),
1167        id: arg.id.clone(),
1168    });
1169    call_data
1170        .encode_as_fields_to(&mut fields, type_resolver, out)
1171        .map_err(ExtrinsicEncodeError::CannotEncodeCallData)?;
1172
1173    Ok(())
1174}
1175
1176// V4 unsigned and V5 bare extrinsics are basically encoded
1177// in the same way; this helper can do either.
1178fn encode_unsigned_at_version_with_info_to<CallData, Resolver>(
1179    call_data: &CallData,
1180    call_info: &ExtrinsicCallInfo<Resolver::TypeId>,
1181    type_resolver: &Resolver,
1182    tx_version: TransactionVersion,
1183    out: &mut Vec<u8>,
1184) -> Result<(), ExtrinsicEncodeError>
1185where
1186    Resolver: TypeResolver,
1187    CallData: EncodeAsFields,
1188{
1189    // Build our inner, non-length-prefixed extrinsic:
1190    let inner = {
1191        let mut out = Vec::new();
1192        // Transaction version (4):
1193        (tx_version as u8).encode_to(&mut out);
1194        // Then the arguments for the call:
1195        encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut out)?;
1196        out
1197    };
1198
1199    // Encode the inner vec to prefix the compact length to it:
1200    inner.encode_to(out);
1201    Ok(())
1202}
1203
1204#[derive(Copy, Clone)]
1205#[repr(u8)]
1206enum TransactionVersion {
1207    V4 = 4u8,
1208    V5 = 5u8,
1209}
1210
1211/// Encode the transaction extension values to the provided output in the order given by
1212/// the extension args from an [`ExtrinsicExtensionInfo`]. We skip missing extensions that
1213/// would encode as 0 bytes, and we encode a 0u8 for any missing extensions whose value is
1214/// an `Option<T>`, which carries the assumption that the default value for such an extension
1215/// is `None`.
1216fn encode_transaction_extension_values<Resolver, Exts>(
1217    extension_ids: &[ExtrinsicExtensionInfoArg<'_, <Resolver as TypeResolver>::TypeId>],
1218    transaction_extensions: &Exts,
1219    type_resolver: &Resolver,
1220    out: &mut Vec<u8>,
1221) -> Result<(), TransactionExtensionsError>
1222where
1223    Resolver: TypeResolver,
1224    Exts: TransactionExtensions<Resolver>,
1225{
1226    let nonempty_values = extension_ids
1227        .iter()
1228        .filter(|arg| !is_type_empty(arg.id.clone(), type_resolver))
1229        .map(|arg| (&*arg.name, arg.id.clone()));
1230
1231    for (name, id) in nonempty_values {
1232        let res =
1233            transaction_extensions.encode_extension_value_to(name, id.clone(), type_resolver, out);
1234
1235        match res {
1236            // All ok
1237            Ok(()) => {}
1238            // Extension not found. As a fallback, if it contains an "Option" then default it to None and encode that.
1239            Err(TransactionExtensionsError::NotFound(name)) => {
1240                if is_type_option(id, type_resolver) {
1241                    0u8.encode_to(out);
1242                } else {
1243                    return Err(TransactionExtensionsError::NotFound(name));
1244                }
1245            }
1246            // Some other error was encountered; return it immediately.
1247            Err(e) => return Err(e),
1248        }
1249    }
1250    Ok(())
1251}
1252
1253/// Encode the transaction extension implicits to the provided output in the order given by
1254/// the extension args from an [`ExtrinsicExtensionInfo`]. We skip missing extensions whose
1255/// implicit would encode as 0 bytes.
1256fn encode_transaction_extension_implicits<Resolver, Exts>(
1257    extension_ids: &[ExtrinsicExtensionInfoArg<'_, <Resolver as TypeResolver>::TypeId>],
1258    transaction_extensions: &Exts,
1259    type_resolver: &Resolver,
1260    out: &mut Vec<u8>,
1261) -> Result<(), TransactionExtensionsError>
1262where
1263    Resolver: TypeResolver,
1264    Exts: TransactionExtensions<Resolver>,
1265{
1266    let nonempty_implicits = extension_ids
1267        .iter()
1268        .filter(|arg| !is_type_empty(arg.implicit_id.clone(), type_resolver))
1269        .map(|arg| (&*arg.name, arg.implicit_id.clone()));
1270
1271    for (name, id) in nonempty_implicits {
1272        transaction_extensions.encode_extension_implicit_to(name, id, type_resolver, out)?;
1273    }
1274    Ok(())
1275}
1276
1277/// Checks to see whether the type being given is empty, ie would require
1278/// 0 bytes to encode. We use this to skip 0 byte transaction extensions; ones
1279/// that are mentioned in the metadata but only used in the node side and require
1280/// no bytes to be given.
1281fn is_type_empty<Resolver: TypeResolver>(type_id: Resolver::TypeId, types: &Resolver) -> bool {
1282    struct IsEmptyVisitor<'r, R> {
1283        types: &'r R,
1284    }
1285    impl<'r, R: TypeResolver> scale_type_resolver::ResolvedTypeVisitor<'r> for IsEmptyVisitor<'r, R> {
1286        type TypeId = R::TypeId;
1287        type Value = bool;
1288
1289        // The default ans safe assumption is that a type is _not_ empty.
1290        fn visit_unhandled(self, _: scale_type_resolver::UnhandledKind) -> Self::Value {
1291            false
1292        }
1293        // Arrays are empty if they are 0 length or the type inside is empty.
1294        fn visit_array(self, type_id: Self::TypeId, len: usize) -> Self::Value {
1295            len == 0 || is_type_empty(type_id, self.types)
1296        }
1297        // Composites are empty if all of their fields are empty.
1298        fn visit_composite<Path, Fields>(self, _path: Path, mut fields: Fields) -> Self::Value
1299        where
1300            Path: scale_type_resolver::PathIter<'r>,
1301            Fields: scale_decode::FieldIter<'r, Self::TypeId>,
1302        {
1303            fields.all(|f| is_type_empty(f.id, self.types))
1304        }
1305        // Tuples are empty if all of their fields are empty.
1306        fn visit_tuple<TypeIds>(self, mut type_ids: TypeIds) -> Self::Value
1307        where
1308            TypeIds: ExactSizeIterator<Item = Self::TypeId>,
1309        {
1310            type_ids.all(|id| is_type_empty(id, self.types))
1311        }
1312    }
1313
1314    types
1315        .resolve_type(type_id, IsEmptyVisitor { types })
1316        .unwrap_or_default()
1317}
1318
1319/// Checks to see whether a type (or the type inside) resolves to being an Option<T>.
1320fn is_type_option<Resolver: TypeResolver>(type_id: Resolver::TypeId, types: &Resolver) -> bool {
1321    struct IsOptionVisitor<'r, R> {
1322        types: &'r R,
1323    }
1324    impl<'r, R: TypeResolver> scale_type_resolver::ResolvedTypeVisitor<'r> for IsOptionVisitor<'r, R> {
1325        type TypeId = R::TypeId;
1326        type Value = bool;
1327
1328        // The default ans safe assumption is that a type is _not_ an option.
1329        fn visit_unhandled(self, _: scale_type_resolver::UnhandledKind) -> Self::Value {
1330            false
1331        }
1332        // Composites are options if they contain exactly one field which is_type_option
1333        fn visit_composite<Path, Fields>(self, _path: Path, mut fields: Fields) -> Self::Value
1334        where
1335            Path: scale_type_resolver::PathIter<'r>,
1336            Fields: scale_decode::FieldIter<'r, Self::TypeId>,
1337        {
1338            match (fields.next(), fields.next()) {
1339                (Some(f), None) => is_type_option(f.id, self.types),
1340                _ => false,
1341            }
1342        }
1343        // Tuples are options if they contain exactly one field which is_type_option
1344        fn visit_tuple<TypeIds>(self, mut type_ids: TypeIds) -> Self::Value
1345        where
1346            TypeIds: ExactSizeIterator<Item = Self::TypeId>,
1347        {
1348            match (type_ids.next(), type_ids.next()) {
1349                (Some(id), None) => is_type_option(id, self.types),
1350                _ => false,
1351            }
1352        }
1353        // Variants are Options if the path is exactly ["Option"] and they contain a None variant at index 0.
1354        fn visit_variant<Path, Fields, Var>(self, mut path: Path, mut variants: Var) -> Self::Value
1355        where
1356            Path: scale_type_resolver::PathIter<'r>,
1357            Fields: scale_decode::FieldIter<'r, Self::TypeId>,
1358            Var: scale_type_resolver::VariantIter<'r, Fields>,
1359        {
1360            match (path.next(), path.next()) {
1361                // If the path exactly ["Option"]?
1362                (Some("Option"), None) => {
1363                    // For a bit more safety: does the option have 2 variants, and a None variant at index 0?
1364                    match (variants.next(), variants.next(), variants.next()) {
1365                        (Some(v1), Some(v2), None) => {
1366                            (v1.name == "None" && v1.index == 0)
1367                                || (v2.name == "None" && v2.index == 0)
1368                        }
1369                        _ => false,
1370                    }
1371                }
1372                _ => false,
1373            }
1374        }
1375    }
1376
1377    types
1378        .resolve_type(type_id, IsOptionVisitor { types })
1379        .unwrap_or_default()
1380}
1381
1382#[cfg(test)]
1383mod test {
1384    use super::*;
1385    use crate::methods::extrinsic_type_info::{ExtrinsicExtensionInfo, ExtrinsicExtensionInfoArg};
1386    use scale_info::PortableRegistry;
1387
1388    /// A test type that implements [`TransactionExtension`] for specific types.
1389    struct TestExtension<Value, Implicit> {
1390        value: Value,
1391        implicit: Implicit,
1392    }
1393
1394    /// A small helper to extract the Value and Implicit type from the above where needed.
1395    trait GetTestExtensionTypes {
1396        type Value;
1397        type Implicit;
1398    }
1399
1400    impl<Value, Implicit> GetTestExtensionTypes for TestExtension<Value, Implicit> {
1401        type Value = Value;
1402        type Implicit = Implicit;
1403    }
1404
1405    /// Create a concrete [`TestExtension`] which implements [`TransactionExtension`].
1406    macro_rules! make_test_extension {
1407        ( $name:ident value=$value:ty, implicit=$implicit:ty ) => {
1408            type $name = TestExtension<$value, $implicit>;
1409            impl TransactionExtension<PortableRegistry> for TestExtension<$value, $implicit> {
1410                const NAME: &str = stringify!($name);
1411                fn encode_value_to(
1412                    &self,
1413                    type_id: u32,
1414                    type_resolver: &PortableRegistry,
1415                    out: &mut Vec<u8>,
1416                ) -> Result<(), TransactionExtensionError> {
1417                    self.value.encode_as_type_to(type_id, type_resolver, out)?;
1418                    Ok(())
1419                }
1420                fn encode_implicit_to(
1421                    &self,
1422                    type_id: u32,
1423                    type_resolver: &PortableRegistry,
1424                    out: &mut Vec<u8>,
1425                ) -> Result<(), TransactionExtensionError> {
1426                    self.implicit
1427                        .encode_as_type_to(type_id, type_resolver, out)?;
1428                    Ok(())
1429                }
1430            }
1431        };
1432    }
1433
1434    #[derive(scale_encode::EncodeAsType, scale_info::TypeInfo, Debug, Clone, PartialEq)]
1435    struct NestedOption<T>(Option<T>);
1436
1437    make_test_extension!(ExtensionContainingOption       value=Option<bool>,          implicit=u64);
1438    make_test_extension!(ExtensionContainingNestedOption value=(NestedOption<bool>,), implicit=u64);
1439    make_test_extension!(ExtensionContainingNothing      value=(),                    implicit=());
1440    make_test_extension!(Extension1                      value=(bool, u64),           implicit=bool);
1441    make_test_extension!(Extension2                      value=String,                implicit=());
1442
1443    /// A test extension which authorizes the transaction, standing in for
1444    /// something like the real `VerifyMultiSignature` extension.
1445    struct VerifyMultiSignature;
1446
1447    impl GetTestExtensionTypes for VerifyMultiSignature {
1448        type Value = ();
1449        type Implicit = ();
1450    }
1451
1452    impl TransactionExtension<PortableRegistry> for VerifyMultiSignature {
1453        const NAME: &str = "VerifyMultiSignature";
1454
1455        fn is_authorization_extension(&self) -> bool {
1456            true
1457        }
1458
1459        fn encode_value_to(
1460            &self,
1461            _type_id: u32,
1462            _type_resolver: &PortableRegistry,
1463            _out: &mut Vec<u8>,
1464        ) -> Result<(), TransactionExtensionError> {
1465            Ok(())
1466        }
1467
1468        fn encode_implicit_to(
1469            &self,
1470            _type_id: u32,
1471            _type_resolver: &PortableRegistry,
1472            _out: &mut Vec<u8>,
1473        ) -> Result<(), TransactionExtensionError> {
1474            Ok(())
1475        }
1476    }
1477
1478    /// Returns an ExtrinsicExtensionInfo vec given some set of test transaction extensions, as well
1479    /// as the PortableRegistry needed to resolve those types properly.
1480    macro_rules! make_extension_info {
1481        ( $($ident:ident),* $(,)? ) => {{
1482            use scale_info::meta_type;
1483            let mut registry = scale_info::Registry::new();
1484            let extension_info = vec![
1485                $(
1486                    ExtrinsicExtensionInfoArg {
1487                        name: <$ident as TransactionExtension<PortableRegistry>>::NAME.into(),
1488                        id: registry.register_type(&meta_type::<<$ident as GetTestExtensionTypes>::Value>()).id,
1489                        implicit_id: registry.register_type(&meta_type::<<$ident as GetTestExtensionTypes>::Implicit>()).id,
1490                    }
1491                ),*
1492            ];
1493
1494            let portable_registry: PortableRegistry = registry.into();
1495            let info = ExtrinsicExtensionInfo { extension_ids: extension_info };
1496            (info, portable_registry)
1497        }}
1498    }
1499
1500    fn assert_decodes_into<T: parity_scale_codec::Decode + std::fmt::Debug + PartialEq>(
1501        bytes: &[u8],
1502        target: T,
1503    ) {
1504        let cursor = &mut &*bytes;
1505        let actual = T::decode(cursor).expect("decoding should succeed");
1506        assert_eq!(actual, target, "actual does not match target");
1507        if !cursor.is_empty() {
1508            panic!("Leftvoer bytes after decoding");
1509        }
1510    }
1511
1512    // -------------------------- And now the actual tests --------------------------
1513
1514    #[test]
1515    fn v5_signer_payload_encodes_the_runtime_implication() {
1516        let (extension_info, types) = make_extension_info![
1517            Extension1,
1518            ExtensionContainingNothing,
1519            VerifyMultiSignature,
1520            ExtensionContainingOption,
1521        ];
1522        let extensions = (
1523            Extension1 {
1524                value: (true, 123),
1525                implicit: false,
1526            },
1527            VerifyMultiSignature,
1528            ExtensionContainingOption {
1529                value: Some(true),
1530                implicit: 12345,
1531            },
1532        );
1533        let call_info = ExtrinsicCallInfo {
1534            pallet_index: 1,
1535            call_index: 2,
1536            pallet_name: "Test".into(),
1537            call_name: "call".into(),
1538            args: Vec::new(),
1539        };
1540
1541        let actual = encode_v5_signer_payload_with_info(
1542            &(),
1543            7,
1544            &extensions,
1545            &types,
1546            &call_info,
1547            &extension_info,
1548        )
1549        .unwrap();
1550
1551        // Mirrors TxBaseImplication((extension_version, call)) followed by the explicit and
1552        // implicit implications strictly after VerifyMultiSignature.
1553        let expected_preimage = (7u8, 1u8, 2u8, Some(true), 12345u64).encode();
1554        let expected = sp_crypto_hashing::blake2_256(&expected_preimage);
1555
1556        assert_eq!(actual, expected);
1557    }
1558
1559    #[test]
1560    fn encode_transaction_extension_values_basic() {
1561        let (info, types) = make_extension_info![Extension1, Extension2,];
1562
1563        let exts = (
1564            Extension1 {
1565                value: (true, 123),
1566                implicit: false,
1567            },
1568            Extension2 {
1569                value: "Hello".to_owned(),
1570                implicit: (),
1571            },
1572        );
1573
1574        let mut out = vec![];
1575        encode_transaction_extension_values(&info.extension_ids, &exts, &types, &mut out)
1576            .expect("Encoding should succeed");
1577        assert_decodes_into(&out, (true, 123u64, "Hello".to_owned()));
1578    }
1579
1580    #[test]
1581    fn encode_transaction_extension_values_order_irrelevant() {
1582        let (info, types) = make_extension_info![Extension1, Extension2,];
1583
1584        let exts = (
1585            Extension2 {
1586                value: "Hello".to_owned(),
1587                implicit: (),
1588            },
1589            Extension1 {
1590                value: (true, 123),
1591                implicit: false,
1592            },
1593        );
1594
1595        let mut out = vec![];
1596        encode_transaction_extension_values(&info.extension_ids, &exts, &types, &mut out)
1597            .expect("Encoding should succeed");
1598        assert_decodes_into(&out, (true, 123u64, "Hello".to_owned()));
1599    }
1600
1601    #[test]
1602    fn encode_transaction_extension_values_skips_empty() {
1603        let (info, types) =
1604            make_extension_info![Extension1, ExtensionContainingNothing, Extension2,];
1605
1606        let exts = (
1607            Extension1 {
1608                value: (true, 123),
1609                implicit: false,
1610            },
1611            Extension2 {
1612                value: "Hello".to_owned(),
1613                implicit: (),
1614            },
1615        );
1616
1617        let mut out = vec![];
1618        encode_transaction_extension_values(&info.extension_ids, &exts, &types, &mut out)
1619            .expect("Encoding should succeed despite Option");
1620        assert_decodes_into(&out, (true, 123u64, "Hello".to_owned()));
1621    }
1622
1623    #[test]
1624    fn encode_transaction_extension_values_skips_option() {
1625        let (info, types) = make_extension_info![
1626            Extension1,
1627            ExtensionContainingOption,
1628            ExtensionContainingNestedOption,
1629            Extension2,
1630        ];
1631
1632        let exts = (
1633            Extension1 {
1634                value: (true, 123),
1635                implicit: false,
1636            },
1637            Extension2 {
1638                value: "Hello".to_owned(),
1639                implicit: (),
1640            },
1641        );
1642
1643        let mut out = vec![];
1644        encode_transaction_extension_values(&info.extension_ids, &exts, &types, &mut out)
1645            .expect("Encoding should succeed despite Option");
1646        assert_decodes_into(&out, (true, 123u64, 0u8, 0u8, "Hello".to_owned()));
1647    }
1648
1649    #[test]
1650    fn encode_transaction_extension_implicits_skips_empty() {
1651        let (info, types) =
1652            make_extension_info![Extension1, Extension2, ExtensionContainingOption,];
1653
1654        let exts = (
1655            Extension1 {
1656                value: (true, 123),
1657                implicit: false,
1658            },
1659            Extension2 {
1660                value: "Hello".to_owned(),
1661                implicit: (),
1662            },
1663            ExtensionContainingOption {
1664                value: None,
1665                implicit: 12345,
1666            },
1667        );
1668
1669        let mut out = vec![];
1670        encode_transaction_extension_implicits(&info.extension_ids, &exts, &types, &mut out)
1671            .expect("Encoding should succeed");
1672        assert_decodes_into(&out, (false, 12345u64));
1673    }
1674
1675    #[test]
1676    fn encode_transaction_extension_implicits_order_irrelevant() {
1677        let (info, types) =
1678            make_extension_info![Extension1, Extension2, ExtensionContainingOption,];
1679
1680        let exts = (
1681            ExtensionContainingOption {
1682                value: None,
1683                implicit: 12345,
1684            },
1685            Extension2 {
1686                value: "Hello".to_owned(),
1687                implicit: (),
1688            },
1689            Extension1 {
1690                value: (true, 123),
1691                implicit: false,
1692            },
1693        );
1694
1695        let mut out = vec![];
1696        encode_transaction_extension_implicits(&info.extension_ids, &exts, &types, &mut out)
1697            .expect("Encoding should succeed");
1698        assert_decodes_into(&out, (false, 12345u64));
1699    }
1700}