1use crate::{
21 generic::{CheckedExtrinsic, ExtrinsicFormat},
22 traits::{
23 self, AsTransactionAuthorizedOrigin, Checkable, DecodeWithVersion,
24 DecodeWithVersionWithMemTracking, Dispatchable, ExtensionVariant, ExtrinsicCall,
25 ExtrinsicLike, ExtrinsicMetadata, IdentifyAccount, InvalidVersion, LazyExtrinsic,
26 MaybeDisplay, Member, Pipeline, PipelineVersion, SignaturePayload, TransactionExtension,
27 },
28 transaction_validity::{InvalidTransaction, TransactionValidityError},
29 OpaqueExtrinsic,
30};
31#[cfg(all(not(feature = "std"), feature = "serde"))]
32use alloc::format;
33use alloc::{vec, vec::Vec};
34use codec::{
35 Compact, CountedInput, Decode, DecodeWithMemLimit, DecodeWithMemTracking, Encode, EncodeLike,
36 Input,
37};
38use core::fmt::{
39 Debug, {self},
40};
41use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter};
42use sp_io::hashing::blake2_256;
43use sp_weights::Weight;
44
45pub type ExtensionVersion = u8;
47pub type ExtrinsicVersion = u8;
49
50pub const EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 5;
56pub const LEGACY_EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 4;
62
63pub const DEFAULT_MAX_CALL_SIZE: usize = 16 * 1024 * 1024; pub type UncheckedSignaturePayload<Address, Signature, Extension> = (Address, Signature, Extension);
68
69impl<Address: TypeInfo, Signature: TypeInfo, Extension: TypeInfo> SignaturePayload
70 for UncheckedSignaturePayload<Address, Signature, Extension>
71{
72 type SignatureAddress = Address;
73 type Signature = Signature;
74 type SignatureExtra = Extension;
75}
76
77#[derive(Eq, PartialEq, Clone)]
88pub enum Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions = InvalidVersion> {
89 Bare(ExtrinsicVersion),
96 Signed(Address, Signature, ExtensionV0),
99 General(ExtensionVariant<ExtensionV0, ExtensionOtherVersions>),
103}
104
105const VERSION_MASK: u8 = 0b0011_1111;
106const TYPE_MASK: u8 = 0b1100_0000;
107const BARE_EXTRINSIC: u8 = 0b0000_0000;
108const SIGNED_EXTRINSIC: u8 = 0b1000_0000;
109const GENERAL_EXTRINSIC: u8 = 0b0100_0000;
110
111impl<Address, Signature, ExtensionV0, ExtensionOtherVersions> Decode
112 for Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>
113where
114 Address: Decode,
115 Signature: Decode,
116 ExtensionV0: Decode,
117 ExtensionOtherVersions: DecodeWithVersion,
118{
119 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
120 let version_and_type = input.read_byte()?;
121
122 let version = version_and_type & VERSION_MASK;
123 let xt_type = version_and_type & TYPE_MASK;
124
125 let preamble = match (version, xt_type) {
126 (
127 extrinsic_version @ LEGACY_EXTRINSIC_FORMAT_VERSION..=EXTRINSIC_FORMAT_VERSION,
128 BARE_EXTRINSIC,
129 ) => Self::Bare(extrinsic_version),
130 (LEGACY_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC) => {
131 let address = Address::decode(input)?;
132 let signature = Signature::decode(input)?;
133 let ext = ExtensionV0::decode(input)?;
134 Self::Signed(address, signature, ext)
135 },
136 (EXTRINSIC_FORMAT_VERSION, GENERAL_EXTRINSIC) => {
137 let ext_version = ExtensionVersion::decode(input)?;
138 let ext =
139 ExtensionVariant::<ExtensionV0, ExtensionOtherVersions>::decode_with_version(
140 ext_version,
141 input,
142 )?;
143 Self::General(ext)
144 },
145 (_, _) => return Err("Invalid transaction version".into()),
146 };
147
148 Ok(preamble)
149 }
150}
151
152impl<Address, Signature, ExtensionV0, ExtensionOtherVersions> DecodeWithMemTracking
153 for Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>
154where
155 Address: DecodeWithMemTracking,
156 Signature: DecodeWithMemTracking,
157 ExtensionV0: DecodeWithMemTracking,
158 ExtensionOtherVersions: DecodeWithVersionWithMemTracking,
159{
160}
161
162impl<Address, Signature, ExtensionV0, ExtensionOtherVersions> Encode
163 for Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>
164where
165 Address: Encode,
166 Signature: Encode,
167 ExtensionV0: Encode,
168 ExtensionOtherVersions: Encode + PipelineVersion,
169{
170 fn size_hint(&self) -> usize {
171 match &self {
172 Preamble::Bare(_) => EXTRINSIC_FORMAT_VERSION.size_hint(),
173 Preamble::Signed(address, signature, ext) => LEGACY_EXTRINSIC_FORMAT_VERSION
174 .size_hint()
175 .saturating_add(address.size_hint())
176 .saturating_add(signature.size_hint())
177 .saturating_add(ext.size_hint()),
178 Preamble::General(ext) => EXTRINSIC_FORMAT_VERSION
179 .size_hint()
180 .saturating_add(0u8.size_hint()) .saturating_add(ext.size_hint()),
182 }
183 }
184
185 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
186 match &self {
187 Preamble::Bare(extrinsic_version) => {
188 (extrinsic_version | BARE_EXTRINSIC).encode_to(dest);
189 },
190 Preamble::Signed(address, signature, ext) => {
191 (LEGACY_EXTRINSIC_FORMAT_VERSION | SIGNED_EXTRINSIC).encode_to(dest);
192 address.encode_to(dest);
193 signature.encode_to(dest);
194 ext.encode_to(dest);
195 },
196 Preamble::General(ext) => {
197 (EXTRINSIC_FORMAT_VERSION | GENERAL_EXTRINSIC).encode_to(dest);
198 ext.version().encode_to(dest);
199 ext.encode_to(dest);
200 },
201 }
202 }
203}
204
205impl<Address, Signature, ExtensionV0, ExtensionOtherVersions>
206 Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>
207{
208 pub fn to_signed(self) -> Option<(Address, Signature, ExtensionV0)> {
210 match self {
211 Self::Signed(a, s, e) => Some((a, s, e)),
212 _ => None,
213 }
214 }
215}
216
217impl<Address, Signature, ExtensionV0, ExtensionOtherVersions> fmt::Debug
218 for Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>
219where
220 Address: fmt::Debug,
221 ExtensionV0: fmt::Debug,
222 ExtensionOtherVersions: fmt::Debug,
223{
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 match self {
226 Self::Bare(_) => write!(f, "Bare"),
227 Self::Signed(address, _, tx_ext) => write!(f, "Signed({:?}, {:?})", address, tx_ext),
228 Self::General(tx_ext) => write!(f, "General({:?})", tx_ext),
229 }
230 }
231}
232
233#[derive(DecodeWithMemTracking, Eq, Clone)]
308#[codec(decode_with_mem_tracking_bound(
309 Address: DecodeWithMemTracking,
310 Call: DecodeWithMemTracking,
311 Signature: DecodeWithMemTracking,
312 ExtensionV0: DecodeWithMemTracking,
313 ExtensionOtherVersions: DecodeWithVersionWithMemTracking,
314))]
315pub struct UncheckedExtrinsic<
316 Address,
317 Call,
318 Signature,
319 ExtensionV0,
320 ExtensionOtherVersions = InvalidVersion,
321 const MAX_CALL_SIZE: usize = DEFAULT_MAX_CALL_SIZE,
322> {
323 pub preamble: Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>,
326 pub function: Call,
328 pub encoded_call: Option<Vec<u8>>,
336 #[codec(skip)]
340 pub encoded_len: Option<usize>,
341}
342
343impl<
344 Address: Debug,
345 Call: Debug,
346 Signature: Debug,
347 ExtensionV0: Debug,
348 ExtensionOtherVersions: Debug,
349 const MAX_CALL_SIZE: usize,
350 > Debug
351 for UncheckedExtrinsic<
352 Address,
353 Call,
354 Signature,
355 ExtensionV0,
356 ExtensionOtherVersions,
357 MAX_CALL_SIZE,
358 >
359{
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 let mut debug_struct = f.debug_struct("UncheckedExtrinsic");
362
363 debug_struct.field("preamble", &self.preamble);
364
365 if self.encoded_len.unwrap_or_default() < 1024 * 1024 {
368 debug_struct.field("function", &self.function)
369 } else {
370 debug_struct.field("function", &"Too big to be printed from the runtime")
371 }
372 .finish()
373 }
374}
375
376impl<
377 Address: PartialEq,
378 Call: PartialEq,
379 Signature: PartialEq,
380 ExtensionV0: PartialEq,
381 ExtensionOtherVersions: PartialEq,
382 const MAX_CALL_SIZE: usize,
383 > PartialEq
384 for UncheckedExtrinsic<
385 Address,
386 Call,
387 Signature,
388 ExtensionV0,
389 ExtensionOtherVersions,
390 MAX_CALL_SIZE,
391 >
392{
393 fn eq(&self, other: &Self) -> bool {
394 self.preamble == other.preamble && self.function == other.function
395 }
396}
397
398impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
403 TypeInfo
404 for UncheckedExtrinsic<
405 Address,
406 Call,
407 Signature,
408 ExtensionV0,
409 ExtensionOtherVersions,
410 MAX_CALL_SIZE,
411 >
412where
413 Address: StaticTypeInfo,
414 Call: StaticTypeInfo,
415 Signature: StaticTypeInfo,
416 ExtensionV0: StaticTypeInfo,
417 ExtensionOtherVersions: StaticTypeInfo,
418{
419 type Identity = UncheckedExtrinsic<
420 Address,
421 Call,
422 Signature,
423 ExtensionV0,
424 ExtensionOtherVersions,
425 MAX_CALL_SIZE,
426 >;
427
428 fn type_info() -> Type {
429 Type::builder()
430 .path(Path::new("UncheckedExtrinsic", module_path!()))
431 .type_params(vec![
435 TypeParameter::new("Address", Some(meta_type::<Address>())),
436 TypeParameter::new("Call", Some(meta_type::<Call>())),
437 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
438 TypeParameter::new("Extra", Some(meta_type::<ExtensionV0>())),
439 ])
445 .docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"])
446 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
450 }
451}
452
453impl<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions, const MAX_CALL_SIZE: usize>
454 UncheckedExtrinsic<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions, MAX_CALL_SIZE>
455{
456 #[deprecated = "Use new_bare instead"]
460 pub fn new_unsigned(function: Call) -> Self {
461 Self::new_bare(function)
462 }
463
464 pub fn is_inherent(&self) -> bool {
466 matches!(self.preamble, Preamble::Bare(_))
467 }
468
469 pub fn is_signed(&self) -> bool {
472 matches!(self.preamble, Preamble::Signed(..))
473 }
474
475 pub fn from_parts(
477 function: Call,
478 preamble: Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>,
479 ) -> Self {
480 Self { preamble, function, encoded_call: None, encoded_len: None }
481 }
482
483 pub fn new_bare(function: Call) -> Self {
485 Self::from_parts(function, Preamble::Bare(EXTRINSIC_FORMAT_VERSION))
486 }
487
488 pub fn new_bare_legacy(function: Call) -> Self {
490 Self::from_parts(function, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION))
491 }
492
493 pub fn new_signed(
495 function: Call,
496 signed: Address,
497 signature: Signature,
498 tx_ext: ExtensionV0,
499 ) -> Self {
500 Self::from_parts(function, Preamble::Signed(signed, signature, tx_ext))
501 }
502
503 pub fn new_transaction(function: Call, tx_ext: ExtensionV0) -> Self {
505 Self::from_parts(function, Preamble::General(ExtensionVariant::V0(tx_ext)))
506 }
507
508 fn decode_with_len<I: Input>(input: &mut I, len: usize) -> Result<Self, codec::Error>
509 where
510 Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>: DecodeWithMemTracking,
511 Call: DecodeWithMemTracking,
512 {
513 let mut input = CountedInput::new(input);
514
515 let preamble =
518 DecodeWithMemLimit::decode_with_mem_limit(&mut input, MAX_CALL_SIZE.saturating_add(1))?;
519
520 struct CloneBytes<'a, I>(&'a mut I, Vec<u8>);
521 impl<I: Input> Input for CloneBytes<'_, I> {
522 fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
523 self.0.remaining_len()
524 }
525
526 fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
527 self.0.read(into)?;
528
529 self.1.extend_from_slice(into);
530 Ok(())
531 }
532
533 fn descend_ref(&mut self) -> Result<(), codec::Error> {
534 self.0.descend_ref()
535 }
536
537 fn ascend_ref(&mut self) {
538 self.0.ascend_ref();
539 }
540
541 fn on_before_alloc_mem(&mut self, size: usize) -> Result<(), codec::Error> {
542 self.0.on_before_alloc_mem(size)
543 }
544 }
545
546 let mut clone_bytes = CloneBytes(&mut input, Vec::with_capacity(len));
547
548 let function =
551 Call::decode_with_mem_limit(&mut clone_bytes, MAX_CALL_SIZE.saturating_add(1))?;
552
553 let encoded_call = Some(clone_bytes.1);
554
555 if input.count() != len as u64 {
556 return Err("Invalid length prefix".into());
557 }
558
559 Ok(Self { preamble, function, encoded_call, encoded_len: Some(len) })
560 }
561
562 fn encode_without_prefix(&self) -> Vec<u8>
563 where
564 Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>: Encode,
565 Call: Encode,
566 {
567 let mut encoded = self.preamble.encode();
568
569 match &self.encoded_call {
570 Some(call) => {
571 encoded.extend(call);
572 },
573 None => {
574 self.function.encode_to(&mut encoded);
575 },
576 }
577
578 encoded
579 }
580}
581
582impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
583 ExtrinsicLike
584 for UncheckedExtrinsic<
585 Address,
586 Call,
587 Signature,
588 ExtensionV0,
589 ExtensionOtherVersions,
590 MAX_CALL_SIZE,
591 >
592{
593 fn is_signed(&self) -> Option<bool> {
594 Some(matches!(self.preamble, Preamble::Signed(..)))
595 }
596
597 fn is_bare(&self) -> bool {
598 matches!(self.preamble, Preamble::Bare(_))
599 }
600}
601
602impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
603 ExtrinsicCall
604 for UncheckedExtrinsic<
605 Address,
606 Call,
607 Signature,
608 ExtensionV0,
609 ExtensionOtherVersions,
610 MAX_CALL_SIZE,
611 >
612{
613 type Call = Call;
614
615 fn call(&self) -> &Call {
616 &self.function
617 }
618
619 fn into_call(self) -> Self::Call {
620 self.function
621 }
622}
623
624impl<
629 LookupSource,
630 AccountId,
631 Call,
632 Signature,
633 ExtensionV0,
634 const MAX_CALL_SIZE: usize,
635 ExtensionOtherVersions,
636 Lookup,
637 > Checkable<Lookup>
638 for UncheckedExtrinsic<
639 LookupSource,
640 Call,
641 Signature,
642 ExtensionV0,
643 ExtensionOtherVersions,
644 MAX_CALL_SIZE,
645 >
646where
647 LookupSource: Member + MaybeDisplay,
648 Call: Encode + Member + Dispatchable,
649 Signature: Member + traits::Verify,
650 <Signature as traits::Verify>::Signer: IdentifyAccount<AccountId = AccountId>,
651 ExtensionV0: Encode + TransactionExtension<Call>,
652 AccountId: Member + MaybeDisplay,
653 Lookup: traits::Lookup<Source = LookupSource, Target = AccountId>,
654{
655 type Checked = CheckedExtrinsic<AccountId, Call, ExtensionV0, ExtensionOtherVersions>;
656
657 fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
658 Ok(match self.preamble {
659 Preamble::Signed(signed, signature, tx_ext) => {
660 let signed = lookup.lookup(signed)?;
661 let raw_payload = SignedPayload::new(
663 CallAndMaybeEncoded { encoded: self.encoded_call, call: self.function },
664 tx_ext,
665 )?;
666
667 let mut payload = Vec::with_capacity(self.encoded_len.unwrap_or_default());
668 raw_payload.0.encode_to(&mut payload);
669
670 let payload =
671 if payload.len() > 256 { blake2_256(&payload).to_vec() } else { payload };
672
673 if !signature.verify(payload.as_ref(), &signed) {
674 return Err(InvalidTransaction::BadProof.into());
675 }
676
677 let (function, tx_ext, _) = raw_payload.deconstruct();
678 CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function }
679 },
680 Preamble::General(tx_ext) => CheckedExtrinsic {
681 format: ExtrinsicFormat::General(tx_ext),
682 function: self.function,
683 },
684 Preamble::Bare(_) => {
685 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }
686 },
687 })
688 }
689
690 #[cfg(feature = "try-runtime")]
691 fn unchecked_into_checked_i_know_what_i_am_doing(
692 self,
693 lookup: &Lookup,
694 ) -> Result<Self::Checked, TransactionValidityError> {
695 Ok(match self.preamble {
696 Preamble::Signed(signed, _, tx_ext) => {
697 let signed = lookup.lookup(signed)?;
698 CheckedExtrinsic {
699 format: ExtrinsicFormat::Signed(signed, tx_ext),
700 function: self.function,
701 }
702 },
703 Preamble::General(tx_ext) => CheckedExtrinsic {
704 format: ExtrinsicFormat::General(tx_ext),
705 function: self.function,
706 },
707 Preamble::Bare(_) => {
708 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }
709 },
710 })
711 }
712}
713
714impl<
715 Address,
716 Call: Dispatchable,
717 Signature,
718 ExtensionV0,
719 const MAX_CALL_SIZE: usize,
720 ExtensionOtherVersions,
721 > ExtrinsicMetadata
722 for UncheckedExtrinsic<
723 Address,
724 Call,
725 Signature,
726 ExtensionV0,
727 ExtensionOtherVersions,
728 MAX_CALL_SIZE,
729 >
730{
731 const VERSIONS: &'static [u8] = &[LEGACY_EXTRINSIC_FORMAT_VERSION, EXTRINSIC_FORMAT_VERSION];
732 type TransactionExtensionPipelines = ExtensionVariant<ExtensionV0, ExtensionOtherVersions>;
733}
734
735impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
736 UncheckedExtrinsic<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions, MAX_CALL_SIZE>
737where
738 Call: Dispatchable + Encode,
739 ExtensionV0: TransactionExtension<Call>,
740 ExtensionOtherVersions: Pipeline<Call>,
741 <Call as Dispatchable>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
742{
743 pub fn extension_weight(&self) -> Weight {
746 match &self.preamble {
747 Preamble::Bare(_) => Weight::zero(),
748 Preamble::Signed(_, _, ext) => ext.weight(&self.function),
749 Preamble::General(ext) => ext.weight(&self.function),
750 }
751 }
752}
753
754impl<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions, const MAX_CALL_SIZE: usize>
755 Decode
756 for UncheckedExtrinsic<
757 Address,
758 Call,
759 Signature,
760 ExtensionV0,
761 ExtensionOtherVersions,
762 MAX_CALL_SIZE,
763 >
764where
765 Address: DecodeWithMemTracking,
766 Signature: DecodeWithMemTracking,
767 Call: DecodeWithMemTracking,
768 ExtensionV0: DecodeWithMemTracking,
769 ExtensionOtherVersions: DecodeWithVersionWithMemTracking,
770{
771 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
772 let expected_length: Compact<u32> = Decode::decode(input)?;
776
777 Self::decode_with_len(input, expected_length.0 as usize)
778 }
779}
780
781#[docify::export(unchecked_extrinsic_encode_impl)]
782impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
783 Encode
784 for UncheckedExtrinsic<
785 Address,
786 Call,
787 Signature,
788 ExtensionV0,
789 ExtensionOtherVersions,
790 MAX_CALL_SIZE,
791 >
792where
793 Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>: Encode,
794 Call: Encode,
795 ExtensionV0: Encode,
796 ExtensionOtherVersions: Encode,
797{
798 fn encode(&self) -> Vec<u8> {
799 let tmp = self.encode_without_prefix();
800
801 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
802
803 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
805
806 compact_len.encode_to(&mut output);
807 output.extend(tmp);
808
809 output
810 }
811}
812
813impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
814 EncodeLike
815 for UncheckedExtrinsic<
816 Address,
817 Call,
818 Signature,
819 ExtensionV0,
820 ExtensionOtherVersions,
821 MAX_CALL_SIZE,
822 >
823where
824 Self: Encode,
825{
826}
827
828#[cfg(feature = "serde")]
829impl<Address, Signature, Call, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
830 serde::Serialize
831 for UncheckedExtrinsic<
832 Address,
833 Call,
834 Signature,
835 ExtensionV0,
836 ExtensionOtherVersions,
837 MAX_CALL_SIZE,
838 >
839where
840 Address: Encode,
841 Signature: Encode,
842 Call: Encode,
843 ExtensionV0: Encode,
844 ExtensionOtherVersions: Encode + PipelineVersion,
845{
846 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
847 where
848 S: ::serde::Serializer,
849 {
850 self.using_encoded(|bytes| seq.serialize_bytes(bytes))
851 }
852}
853
854#[cfg(feature = "serde")]
855impl<
856 'a,
857 Address,
858 Signature,
859 Call,
860 ExtensionV0,
861 const MAX_CALL_SIZE: usize,
862 ExtensionOtherVersions,
863 > serde::Deserialize<'a>
864 for UncheckedExtrinsic<
865 Address,
866 Call,
867 Signature,
868 ExtensionV0,
869 ExtensionOtherVersions,
870 MAX_CALL_SIZE,
871 >
872where
873 Address: DecodeWithMemTracking,
874 Signature: DecodeWithMemTracking,
875 Call: DecodeWithMemTracking,
876 ExtensionV0: DecodeWithMemTracking,
877 ExtensionOtherVersions: DecodeWithVersionWithMemTracking,
878{
879 fn deserialize<D>(de: D) -> Result<Self, D::Error>
880 where
881 D: serde::Deserializer<'a>,
882 {
883 let r = sp_core::bytes::deserialize(de)?;
884 Self::decode(&mut &r[..])
885 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
886 }
887}
888
889pub struct CallAndMaybeEncoded<T> {
891 encoded: Option<Vec<u8>>,
892 call: T,
893}
894
895impl<T> CallAndMaybeEncoded<T> {
896 pub fn into_call(self) -> T {
898 self.call
899 }
900}
901
902impl<T> From<T> for CallAndMaybeEncoded<T> {
903 fn from(value: T) -> Self {
904 Self { call: value, encoded: None }
905 }
906}
907
908impl<T: Encode> Encode for CallAndMaybeEncoded<T> {
909 fn encode_to<O: codec::Output + ?Sized>(&self, dest: &mut O) {
910 match &self.encoded {
911 Some(enc) => dest.write(enc),
912 None => self.call.encode_to(dest),
913 }
914 }
915
916 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
917 match &self.encoded {
918 Some(enc) => f(&enc),
919 None => self.call.using_encoded(f),
920 }
921 }
922}
923
924pub struct SignedPayload<Call: Dispatchable, ExtensionV0: TransactionExtension<Call>>(
930 (CallAndMaybeEncoded<Call>, ExtensionV0, ExtensionV0::Implicit),
931);
932
933impl<Call, ExtensionV0> SignedPayload<Call, ExtensionV0>
934where
935 Call: Encode + Dispatchable,
936 ExtensionV0: TransactionExtension<Call>,
937{
938 pub fn new(
942 call: impl Into<CallAndMaybeEncoded<Call>>,
943 tx_ext: ExtensionV0,
944 ) -> Result<Self, TransactionValidityError> {
945 let implicit = ExtensionV0::implicit(&tx_ext)?;
946 Ok(Self((call.into(), tx_ext, implicit)))
947 }
948
949 pub fn from_raw(
951 call: impl Into<CallAndMaybeEncoded<Call>>,
952 tx_ext: ExtensionV0,
953 implicit: ExtensionV0::Implicit,
954 ) -> Self {
955 Self((call.into(), tx_ext, implicit))
956 }
957
958 pub fn deconstruct(self) -> (Call, ExtensionV0, ExtensionV0::Implicit) {
960 let (call, ext, implicit) = self.0;
961 (call.call, ext, implicit)
962 }
963}
964
965impl<Call, ExtensionV0> Encode for SignedPayload<Call, ExtensionV0>
966where
967 Call: Encode + Dispatchable,
968 ExtensionV0: TransactionExtension<Call>,
969{
970 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
972 self.0.using_encoded(|payload| {
973 if payload.len() > 256 {
974 f(&blake2_256(payload)[..])
975 } else {
976 f(payload)
977 }
978 })
979 }
980}
981
982impl<Call, ExtensionV0> EncodeLike for SignedPayload<Call, ExtensionV0>
983where
984 Call: Encode + Dispatchable,
985 ExtensionV0: TransactionExtension<Call>,
986{
987}
988
989impl<Address, Call, Signature, ExtensionV0, const MAX_CALL_SIZE: usize, ExtensionOtherVersions>
990 From<
991 UncheckedExtrinsic<
992 Address,
993 Call,
994 Signature,
995 ExtensionV0,
996 ExtensionOtherVersions,
997 MAX_CALL_SIZE,
998 >,
999 > for OpaqueExtrinsic
1000where
1001 Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>: Encode,
1002 Call: Encode,
1003 ExtensionV0: Encode,
1004 ExtensionOtherVersions: Encode + PipelineVersion,
1005{
1006 fn from(
1007 extrinsic: UncheckedExtrinsic<
1008 Address,
1009 Call,
1010 Signature,
1011 ExtensionV0,
1012 ExtensionOtherVersions,
1013 MAX_CALL_SIZE,
1014 >,
1015 ) -> Self {
1016 Self::from_blob(extrinsic.encode_without_prefix())
1017 }
1018}
1019
1020impl<Address, Call, Signature, ExtensionV0, ExtensionOtherVersions, const MAX_CALL_SIZE: usize>
1021 LazyExtrinsic
1022 for UncheckedExtrinsic<
1023 Address,
1024 Call,
1025 Signature,
1026 ExtensionV0,
1027 ExtensionOtherVersions,
1028 MAX_CALL_SIZE,
1029 >
1030where
1031 Preamble<Address, Signature, ExtensionV0, ExtensionOtherVersions>: DecodeWithMemTracking,
1032 Call: DecodeWithMemTracking,
1033 ExtensionV0: DecodeWithMemTracking,
1034 ExtensionOtherVersions: DecodeWithVersionWithMemTracking + PipelineVersion,
1035{
1036 fn decode_unprefixed(data: &[u8]) -> Result<Self, codec::Error> {
1037 Self::decode_with_len(&mut &data[..], data.len())
1038 }
1039}
1040
1041#[cfg(test)]
1042mod legacy {
1043 use codec::{Compact, Decode, Encode, EncodeLike, Error, Input};
1044 use scale_info::{
1045 build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter,
1046 };
1047
1048 pub type UncheckedSignaturePayloadV4<Address, Signature, Extra> = (Address, Signature, Extra);
1049
1050 #[derive(PartialEq, Eq, Clone, Debug)]
1051 pub struct UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
1052 pub signature: Option<UncheckedSignaturePayloadV4<Address, Signature, Extra>>,
1053 pub function: Call,
1054 }
1055
1056 impl<Address, Call, Signature, Extra> TypeInfo
1057 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
1058 where
1059 Address: StaticTypeInfo,
1060 Call: StaticTypeInfo,
1061 Signature: StaticTypeInfo,
1062 Extra: StaticTypeInfo,
1063 {
1064 type Identity = UncheckedExtrinsicV4<Address, Call, Signature, Extra>;
1065
1066 fn type_info() -> Type {
1067 Type::builder()
1068 .path(Path::new("UncheckedExtrinsic", module_path!()))
1069 .type_params(vec![
1074 TypeParameter::new("Address", Some(meta_type::<Address>())),
1075 TypeParameter::new("Call", Some(meta_type::<Call>())),
1076 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
1077 TypeParameter::new("Extra", Some(meta_type::<Extra>())),
1078 ])
1079 .docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"])
1080 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
1084 }
1085 }
1086
1087 impl<Address, Call, Signature, Extra> UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
1088 pub fn new_signed(
1089 function: Call,
1090 signed: Address,
1091 signature: Signature,
1092 extra: Extra,
1093 ) -> Self {
1094 Self { signature: Some((signed, signature, extra)), function }
1095 }
1096
1097 pub fn new_unsigned(function: Call) -> Self {
1098 Self { signature: None, function }
1099 }
1100 }
1101
1102 impl<Address, Call, Signature, Extra> Decode
1103 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
1104 where
1105 Address: Decode,
1106 Signature: Decode,
1107 Call: Decode,
1108 Extra: Decode,
1109 {
1110 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
1111 let expected_length: Compact<u32> = Decode::decode(input)?;
1115 let before_length = input.remaining_len()?;
1116
1117 let version = input.read_byte()?;
1118
1119 let is_signed = version & 0b1000_0000 != 0;
1120 let version = version & 0b0111_1111;
1121 if version != 4u8 {
1122 return Err("Invalid transaction version".into());
1123 }
1124
1125 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
1126 let function = Decode::decode(input)?;
1127
1128 if let Some((before_length, after_length)) =
1129 input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
1130 {
1131 let length = before_length.saturating_sub(after_length);
1132
1133 if length != expected_length.0 as usize {
1134 return Err("Invalid length prefix".into());
1135 }
1136 }
1137
1138 Ok(Self { signature, function })
1139 }
1140 }
1141
1142 #[docify::export(unchecked_extrinsic_encode_impl)]
1143 impl<Address, Call, Signature, Extra> Encode
1144 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
1145 where
1146 Address: Encode,
1147 Signature: Encode,
1148 Call: Encode,
1149 Extra: Encode,
1150 {
1151 fn encode(&self) -> Vec<u8> {
1152 let mut tmp = Vec::with_capacity(core::mem::size_of::<Self>());
1153
1154 match self.signature.as_ref() {
1156 Some(s) => {
1157 tmp.push(4u8 | 0b1000_0000);
1158 s.encode_to(&mut tmp);
1159 },
1160 None => {
1161 tmp.push(4u8 & 0b0111_1111);
1162 },
1163 }
1164 self.function.encode_to(&mut tmp);
1165
1166 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
1167
1168 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
1170
1171 compact_len.encode_to(&mut output);
1172 output.extend(tmp);
1173
1174 output
1175 }
1176 }
1177
1178 impl<Address, Call, Signature, Extra> EncodeLike
1179 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
1180 where
1181 Address: Encode,
1182 Signature: Encode,
1183 Call: Encode,
1184 Extra: Encode,
1185 {
1186 }
1187}
1188
1189#[cfg(test)]
1190mod tests {
1191 use super::{legacy::UncheckedExtrinsicV4, *};
1192 use crate::{
1193 codec::{Decode, Encode},
1194 impl_tx_ext_default,
1195 testing::TestSignature as TestSig,
1196 traits::{FakeDispatchable, IdentityLookup, TransactionExtension},
1197 };
1198 use sp_io::hashing::blake2_256;
1199
1200 type TestContext = IdentityLookup<u64>;
1201 type TestAccountId = u64;
1202
1203 #[derive(Debug, Clone, Eq, PartialEq, Encode, TypeInfo)]
1205 enum Call {
1206 Raw(Vec<u8>),
1207 Sort(Vec<u8>),
1208 }
1209
1210 impl Decode for Call {
1211 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
1212 let variant = input.read_byte()?;
1213 match variant {
1214 0 => {
1215 let data = Vec::<u8>::decode(input)?;
1216 Ok(Call::Raw(data))
1217 },
1218 1 => {
1219 let mut data = Vec::<u8>::decode(input)?;
1220 data.sort();
1222 Ok(Call::Sort(data))
1223 },
1224 _ => Err("Invalid Call variant".into()),
1225 }
1226 }
1227 }
1228
1229 impl DecodeWithMemTracking for Call {}
1230
1231 impl From<Call> for Vec<u8> {
1232 fn from(call: Call) -> Vec<u8> {
1233 match call {
1234 Call::Sort(data) | Call::Raw(data) => data,
1235 }
1236 }
1237 }
1238
1239 impl From<Vec<u8>> for FakeDispatchable<Call> {
1240 fn from(value: Vec<u8>) -> Self {
1241 Self(Call::Raw(value))
1242 }
1243 }
1244
1245 type TestCall = FakeDispatchable<Call>;
1246
1247 const TEST_ACCOUNT: TestAccountId = 0;
1248
1249 #[derive(
1251 Debug,
1252 Encode,
1253 Decode,
1254 DecodeWithMemTracking,
1255 Clone,
1256 Eq,
1257 PartialEq,
1258 Ord,
1259 PartialOrd,
1260 TypeInfo,
1261 )]
1262 struct DummyExtension;
1263 impl TransactionExtension<TestCall> for DummyExtension {
1264 const IDENTIFIER: &'static str = "DummyExtension";
1265 type Implicit = ();
1266 type Val = ();
1267 type Pre = ();
1268 impl_tx_ext_default!(TestCall; weight validate prepare);
1269 }
1270
1271 type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, DummyExtension>;
1272 type CEx = CheckedExtrinsic<TestAccountId, TestCall, DummyExtension>;
1273
1274 #[test]
1275 fn unsigned_codec_should_work() {
1276 let call: TestCall = Call::Raw(vec![0u8; 0]).into();
1277 let ux = Ex::new_bare(call);
1278 let encoded = ux.encode();
1279 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1280 }
1281
1282 #[test]
1283 fn invalid_length_prefix_is_detected() {
1284 let ux = Ex::new_bare(Call::Raw(vec![0u8; 0]).into());
1285 let mut encoded = ux.encode();
1286
1287 let length = Compact::<u32>::decode(&mut &encoded[..]).unwrap();
1288 Compact(length.0 + 10).encode_to(&mut &mut encoded[..1]);
1289
1290 assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into()));
1291 }
1292
1293 #[test]
1294 fn transaction_codec_should_work() {
1295 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
1296 let encoded = ux.encode();
1297 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1298 }
1299
1300 #[test]
1301 fn signed_codec_should_work() {
1302 let ux = Ex::new_signed(
1303 vec![0u8; 0].into(),
1304 TEST_ACCOUNT,
1305 TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()),
1306 DummyExtension,
1307 );
1308 let encoded = ux.encode();
1309 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1310 }
1311
1312 #[test]
1313 fn large_signed_codec_should_work() {
1314 let ux = Ex::new_signed(
1315 vec![0u8; 0].into(),
1316 TEST_ACCOUNT,
1317 TestSig(
1318 TEST_ACCOUNT,
1319 (vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(),
1320 ),
1321 DummyExtension,
1322 );
1323 let encoded = ux.encode();
1324 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1325 }
1326
1327 #[test]
1328 fn unsigned_check_should_work() {
1329 let ux = Ex::new_bare(vec![0u8; 0].into());
1330 assert!(ux.is_inherent());
1331 assert_eq!(
1332 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1333 Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }),
1334 );
1335 }
1336
1337 #[test]
1338 fn badly_signed_check_should_fail() {
1339 let ux = Ex::new_signed(
1340 vec![0u8; 0].into(),
1341 TEST_ACCOUNT,
1342 TestSig(TEST_ACCOUNT, vec![0u8; 0].into()),
1343 DummyExtension,
1344 );
1345 assert!(!ux.is_inherent());
1346 assert_eq!(
1347 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1348 Err(InvalidTransaction::BadProof.into()),
1349 );
1350 }
1351
1352 #[test]
1353 fn transaction_check_should_work() {
1354 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
1355 assert!(!ux.is_inherent());
1356 assert_eq!(
1357 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1358 Ok(CEx {
1359 format: ExtrinsicFormat::General(ExtensionVariant::V0(DummyExtension)),
1360 function: vec![0u8; 0].into()
1361 }),
1362 );
1363 }
1364
1365 #[test]
1366 fn signed_check_should_work() {
1367 let sig_payload = SignedPayload::from_raw(
1368 FakeDispatchable::from(vec![0u8; 0]),
1369 DummyExtension,
1370 DummyExtension.implicit().unwrap(),
1371 );
1372 let ux = Ex::new_signed(
1373 vec![0u8; 0].into(),
1374 TEST_ACCOUNT,
1375 TestSig(TEST_ACCOUNT, sig_payload.encode()),
1376 DummyExtension,
1377 );
1378 assert!(!ux.is_inherent());
1379 assert_eq!(
1380 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1381 Ok(CEx {
1382 format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension),
1383 function: Call::Raw(vec![0u8; 0]).into()
1384 }),
1385 );
1386 }
1387
1388 #[test]
1389 fn large_signed_check_uses_blake2_256_hashed_payload() {
1390 let large_call_data = vec![0u8; 257];
1395 let call: TestCall = large_call_data.clone().into();
1396 let sig_payload = SignedPayload::from_raw(
1397 call.clone(),
1398 DummyExtension,
1399 DummyExtension.implicit().unwrap(),
1400 );
1401
1402 let raw_payload = (call.clone(), DummyExtension, ()).encode();
1405 assert!(raw_payload.len() > 256);
1406 let signed_bytes = sig_payload.encode();
1407 assert_eq!(signed_bytes.len(), 32);
1408 assert_eq!(signed_bytes, blake2_256(&raw_payload).to_vec());
1409
1410 let ux =
1411 Ex::new_signed(call, TEST_ACCOUNT, TestSig(TEST_ACCOUNT, signed_bytes), DummyExtension);
1412 assert!(!ux.is_inherent());
1413 assert_eq!(
1414 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1415 Ok(CEx {
1416 format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension),
1417 function: Call::Raw(large_call_data).into(),
1418 }),
1419 );
1420 }
1421
1422 #[test]
1423 fn large_signed_check_fails_if_signed_over_unhashed_payload() {
1424 let large_call_data = vec![0u8; 257];
1428 let call: TestCall = large_call_data.into();
1429 let raw_payload = (call.clone(), DummyExtension, ()).encode();
1430 assert!(raw_payload.len() > 256);
1431
1432 let ux =
1433 Ex::new_signed(call, TEST_ACCOUNT, TestSig(TEST_ACCOUNT, raw_payload), DummyExtension);
1434 assert_eq!(
1435 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1436 Err(InvalidTransaction::BadProof.into()),
1437 );
1438 }
1439
1440 #[test]
1441 fn encoding_matches_vec() {
1442 let ex = Ex::new_bare(Call::Raw(vec![0u8; 0]).into());
1443 let encoded = ex.encode();
1444 let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
1445 assert_eq!(decoded, ex);
1446 let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
1447 assert_eq!(as_vec.encode(), encoded);
1448 }
1449
1450 #[test]
1451 fn conversion_to_opaque() {
1452 let ux = Ex::new_bare(Call::Raw(vec![0u8; 0]).into());
1453 let encoded = ux.encode();
1454 let opaque: OpaqueExtrinsic = ux.into();
1455 let opaque_encoded = opaque.encode();
1456 assert_eq!(opaque_encoded, encoded);
1457 }
1458
1459 #[test]
1460 fn large_bad_prefix_should_work() {
1461 let encoded = (Compact::<u32>::from(u32::MAX), Preamble::<(), (), ()>::Bare(0)).encode();
1462 assert!(Ex::decode(&mut &encoded[..]).is_err());
1463 }
1464
1465 #[test]
1466 fn legacy_short_signed_encode_decode() {
1467 let call: TestCall = Call::Raw(vec![0u8; 4]).into();
1468 let signed = TEST_ACCOUNT;
1469 let extension = DummyExtension;
1470 let implicit = extension.implicit().unwrap();
1471 let legacy_signature = TestSig(TEST_ACCOUNT, (&call, &extension, &implicit).encode());
1472
1473 let old_ux =
1474 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
1475 call.clone(),
1476 signed,
1477 legacy_signature.clone(),
1478 extension.clone(),
1479 );
1480
1481 let encoded_old_ux = old_ux.encode();
1482 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1483
1484 assert_eq!(decoded_old_ux.function, call);
1485 assert_eq!(
1486 decoded_old_ux.preamble,
1487 Preamble::Signed(signed, legacy_signature.clone(), extension.clone())
1488 );
1489
1490 let new_ux =
1491 Ex::new_signed(call.clone(), signed, legacy_signature.clone(), extension.clone());
1492
1493 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1494 let old_checked =
1495 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1496 assert_eq!(new_checked, old_checked);
1497 }
1498
1499 #[test]
1500 fn legacy_long_signed_encode_decode() {
1501 let call: TestCall = Call::Raw(vec![0u8; 257]).into();
1502 let signed = TEST_ACCOUNT;
1503 let extension = DummyExtension;
1504 let implicit = extension.implicit().unwrap();
1505 let signature = TestSig(
1506 TEST_ACCOUNT,
1507 blake2_256(&(&call, DummyExtension, &implicit).encode()[..]).to_vec(),
1508 );
1509
1510 let old_ux =
1511 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
1512 call.clone(),
1513 signed,
1514 signature.clone(),
1515 extension.clone(),
1516 );
1517
1518 let encoded_old_ux = old_ux.encode();
1519 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1520
1521 assert_eq!(decoded_old_ux.function, call);
1522 assert_eq!(
1523 decoded_old_ux.preamble,
1524 Preamble::Signed(signed, signature.clone(), extension.clone())
1525 );
1526
1527 let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone());
1528
1529 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1530 let old_checked =
1531 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1532 assert_eq!(new_checked, old_checked);
1533 }
1534
1535 #[test]
1536 fn legacy_unsigned_encode_decode() {
1537 let call: TestCall = Call::Raw(vec![0u8; 0]).into();
1538
1539 let old_ux =
1540 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_unsigned(
1541 call.clone(),
1542 );
1543
1544 let encoded_old_ux = old_ux.encode();
1545 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1546
1547 assert_eq!(decoded_old_ux.function, call);
1548 assert_eq!(decoded_old_ux.preamble, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION));
1549
1550 let new_legacy_ux = Ex::new_bare_legacy(call.clone());
1551 assert_eq!(encoded_old_ux, new_legacy_ux.encode());
1552
1553 let new_ux = Ex::new_bare(call.clone());
1554 let encoded_new_ux = new_ux.encode();
1555 let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap();
1556 assert_eq!(new_ux, decoded_new_ux);
1557
1558 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1559 let old_checked =
1560 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1561 assert_eq!(new_checked, old_checked);
1562 }
1563
1564 #[test]
1565 fn max_call_heap_size_should_be_checked() {
1566 let ux = Ex::new_bare(Call::Raw(vec![0u8; DEFAULT_MAX_CALL_SIZE]).into());
1569 let encoded = ux.encode();
1570 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1571
1572 let ux = Ex::new_bare(Call::Raw(vec![0u8; DEFAULT_MAX_CALL_SIZE + 1]).into());
1574 let encoded = ux.encode();
1575 assert_eq!(
1576 Ex::decode(&mut &encoded[..]).unwrap_err().to_string(),
1577 "Could not decode `FakeDispatchable.0`:\n\tHeap memory limit exceeded while decoding\n"
1578 );
1579 }
1580
1581 #[test]
1584 fn encoding_is_stable() {
1585 let unsorted_data = vec![5u8, 3, 7, 1, 9, 2, 8, 4, 6, 0];
1587 let call = Call::Sort(unsorted_data.clone());
1588
1589 let unsorted_encoded = call.encode();
1590
1591 let sig_payload = SignedPayload::from_raw(
1592 FakeDispatchable::from(call.clone()),
1593 DummyExtension,
1594 DummyExtension.implicit().unwrap(),
1595 );
1596 let sig_payload_encoded = sig_payload.encode();
1597
1598 let ux = Ex::new_signed(
1599 call.into(),
1600 TEST_ACCOUNT,
1601 TestSig(TEST_ACCOUNT, sig_payload_encoded.clone()),
1602 DummyExtension,
1603 );
1604
1605 let encoded = ux.encode();
1608 let decoded_ux = Ex::decode(&mut &encoded[..]).unwrap();
1609
1610 let mut expected_sorted_data = unsorted_data;
1612 expected_sorted_data.sort();
1613
1614 let expected_decoded_call =
1615 FakeDispatchable::from(Call::Sort(expected_sorted_data.clone()));
1616 assert_eq!(decoded_ux.function, expected_decoded_call);
1617
1618 let sorted_encoded = Call::Sort(expected_sorted_data).encode();
1620 assert_ne!(
1621 unsorted_encoded, sorted_encoded,
1622 "Sorted and unsorted should encode differently"
1623 );
1624
1625 assert_eq!(
1627 <Ex as Checkable<TestContext>>::check(decoded_ux, &Default::default())
1628 .unwrap()
1629 .function,
1630 expected_decoded_call,
1631 )
1632 }
1633}