1mod 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#[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
51pub 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
104pub 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
155pub 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#[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#[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#[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 let mut encoded_inner = Vec::new();
362
363 (0b10000000 + 4u8).encode_to(&mut encoded_inner);
365
366 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
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 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 encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut encoded_inner)?;
395
396 encoded_inner.encode_to(out);
398 Ok(())
399}
400
401pub 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
471pub 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 encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut out)?;
496
497 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 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 if out.len() > 256 {
517 out = sp_crypto_hashing::blake2_256(&out).to_vec();
518 }
519
520 Ok(out)
521}
522
523pub 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
579pub 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
630pub 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
655pub 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 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 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 have_data {
733 return Ok(ext_version);
734 }
735 }
736
737 Err(ExtrinsicEncodeError::CannotFindGoodExtensionVersion)
738}
739
740pub 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#[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
885pub 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 let mut encoded_inner = Vec::new();
906
907 (0b01000000 + 5u8).encode_to(&mut encoded_inner);
909
910 transaction_extension_version.encode_to(&mut encoded_inner);
912
913 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 encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut encoded_inner)?;
924
925 encoded_inner.encode_to(out);
927 Ok(())
928}
929
930pub 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
1013pub 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 transaction_extension_version.encode_to(&mut payload);
1041 encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut payload)?;
1042
1043 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 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 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 Ok(sp_crypto_hashing::blake2_256(&payload))
1073}
1074
1075pub 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
1102pub 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
1127pub 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
1145pub 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 call_info.pallet_index.encode_to(out);
1162 call_info.call_index.encode_to(out);
1163
1164 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
1176fn 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 let inner = {
1191 let mut out = Vec::new();
1192 (tx_version as u8).encode_to(&mut out);
1194 encode_call_data_with_info_to(call_data, call_info, type_resolver, &mut out)?;
1196 out
1197 };
1198
1199 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
1211fn 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 Ok(()) => {}
1238 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 Err(e) => return Err(e),
1248 }
1249 }
1250 Ok(())
1251}
1252
1253fn 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
1277fn 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 fn visit_unhandled(self, _: scale_type_resolver::UnhandledKind) -> Self::Value {
1291 false
1292 }
1293 fn visit_array(self, type_id: Self::TypeId, len: usize) -> Self::Value {
1295 len == 0 || is_type_empty(type_id, self.types)
1296 }
1297 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 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
1319fn 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 fn visit_unhandled(self, _: scale_type_resolver::UnhandledKind) -> Self::Value {
1330 false
1331 }
1332 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 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 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 (Some("Option"), None) => {
1363 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 struct TestExtension<Value, Implicit> {
1390 value: Value,
1391 implicit: Implicit,
1392 }
1393
1394 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 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 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 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 #[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 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}