1use crate::{
21 generic::{CheckedExtrinsic, ExtrinsicFormat},
22 traits::{
23 self, transaction_extension::TransactionExtension, Checkable, Dispatchable, ExtrinsicCall,
24 ExtrinsicLike, ExtrinsicMetadata, IdentifyAccount, LazyExtrinsic, MaybeDisplay, Member,
25 SignaturePayload,
26 },
27 transaction_validity::{InvalidTransaction, TransactionValidityError},
28 OpaqueExtrinsic,
29};
30#[cfg(all(not(feature = "std"), feature = "serde"))]
31use alloc::format;
32use alloc::{vec, vec::Vec};
33use codec::{
34 Compact, CountedInput, Decode, DecodeWithMemLimit, DecodeWithMemTracking, Encode, EncodeLike,
35 Input,
36};
37use core::fmt;
38use pezsp_io::hashing::blake2_256;
39use pezsp_weights::Weight;
40use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter};
41
42pub type ExtensionVersion = u8;
44pub type ExtrinsicVersion = u8;
46
47pub const EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 5;
53pub const LEGACY_EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 4;
59const EXTENSION_VERSION: ExtensionVersion = 0;
65
66pub const DEFAULT_MAX_CALL_SIZE: usize = 16 * 1024 * 1024; pub type UncheckedSignaturePayload<Address, Signature, Extension> = (Address, Signature, Extension);
71
72impl<Address: TypeInfo, Signature: TypeInfo, Extension: TypeInfo> SignaturePayload
73 for UncheckedSignaturePayload<Address, Signature, Extension>
74{
75 type SignatureAddress = Address;
76 type Signature = Signature;
77 type SignatureExtra = Extension;
78}
79
80#[derive(DecodeWithMemTracking, Eq, PartialEq, Clone)]
83pub enum Preamble<Address, Signature, Extension> {
84 Bare(ExtrinsicVersion),
91 Signed(Address, Signature, Extension),
94 General(ExtensionVersion, Extension),
98}
99
100const VERSION_MASK: u8 = 0b0011_1111;
101const TYPE_MASK: u8 = 0b1100_0000;
102const BARE_EXTRINSIC: u8 = 0b0000_0000;
103const SIGNED_EXTRINSIC: u8 = 0b1000_0000;
104const GENERAL_EXTRINSIC: u8 = 0b0100_0000;
105
106impl<Address, Signature, Extension> Decode for Preamble<Address, Signature, Extension>
107where
108 Address: Decode,
109 Signature: Decode,
110 Extension: Decode,
111{
112 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
113 let version_and_type = input.read_byte()?;
114
115 let version = version_and_type & VERSION_MASK;
116 let xt_type = version_and_type & TYPE_MASK;
117
118 let preamble = match (version, xt_type) {
119 (
120 extrinsic_version @ LEGACY_EXTRINSIC_FORMAT_VERSION..=EXTRINSIC_FORMAT_VERSION,
121 BARE_EXTRINSIC,
122 ) => Self::Bare(extrinsic_version),
123 (LEGACY_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC) => {
124 let address = Address::decode(input)?;
125 let signature = Signature::decode(input)?;
126 let ext = Extension::decode(input)?;
127 Self::Signed(address, signature, ext)
128 },
129 (EXTRINSIC_FORMAT_VERSION, GENERAL_EXTRINSIC) => {
130 let ext_version = ExtensionVersion::decode(input)?;
131 let ext = Extension::decode(input)?;
132 Self::General(ext_version, ext)
133 },
134 (_, _) => return Err("Invalid transaction version".into()),
135 };
136
137 Ok(preamble)
138 }
139}
140
141impl<Address, Signature, Extension> Encode for Preamble<Address, Signature, Extension>
142where
143 Address: Encode,
144 Signature: Encode,
145 Extension: Encode,
146{
147 fn size_hint(&self) -> usize {
148 match &self {
149 Preamble::Bare(_) => EXTRINSIC_FORMAT_VERSION.size_hint(),
150 Preamble::Signed(address, signature, ext) => LEGACY_EXTRINSIC_FORMAT_VERSION
151 .size_hint()
152 .saturating_add(address.size_hint())
153 .saturating_add(signature.size_hint())
154 .saturating_add(ext.size_hint()),
155 Preamble::General(ext_version, ext) => EXTRINSIC_FORMAT_VERSION
156 .size_hint()
157 .saturating_add(ext_version.size_hint())
158 .saturating_add(ext.size_hint()),
159 }
160 }
161
162 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
163 match &self {
164 Preamble::Bare(extrinsic_version) => {
165 (extrinsic_version | BARE_EXTRINSIC).encode_to(dest);
166 },
167 Preamble::Signed(address, signature, ext) => {
168 (LEGACY_EXTRINSIC_FORMAT_VERSION | SIGNED_EXTRINSIC).encode_to(dest);
169 address.encode_to(dest);
170 signature.encode_to(dest);
171 ext.encode_to(dest);
172 },
173 Preamble::General(ext_version, ext) => {
174 (EXTRINSIC_FORMAT_VERSION | GENERAL_EXTRINSIC).encode_to(dest);
175 ext_version.encode_to(dest);
176 ext.encode_to(dest);
177 },
178 }
179 }
180}
181
182impl<Address, Signature, Extension> Preamble<Address, Signature, Extension> {
183 pub fn to_signed(self) -> Option<(Address, Signature, Extension)> {
185 match self {
186 Self::Signed(a, s, e) => Some((a, s, e)),
187 _ => None,
188 }
189 }
190}
191
192impl<Address, Signature, Extension> fmt::Debug for Preamble<Address, Signature, Extension>
193where
194 Address: fmt::Debug,
195 Extension: fmt::Debug,
196{
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198 match self {
199 Self::Bare(_) => write!(f, "Bare"),
200 Self::Signed(address, _, tx_ext) => write!(f, "Signed({:?}, {:?})", address, tx_ext),
201 Self::General(ext_version, tx_ext) => {
202 write!(f, "General({:?}, {:?})", ext_version, tx_ext)
203 },
204 }
205 }
206}
207
208#[cfg_attr(all(feature = "std", not(windows)), doc = simple_mermaid::mermaid!("../../docs/mermaid/extrinsics.mmd"))]
223#[derive(DecodeWithMemTracking, PartialEq, Eq, Clone, Debug)]
231#[codec(decode_with_mem_tracking_bound(
232 Address: DecodeWithMemTracking,
233 Call: DecodeWithMemTracking,
234 Signature: DecodeWithMemTracking,
235 Extension: DecodeWithMemTracking)
236)]
237pub struct UncheckedExtrinsic<
238 Address,
239 Call,
240 Signature,
241 Extension,
242 const MAX_CALL_SIZE: usize = DEFAULT_MAX_CALL_SIZE,
243> {
244 pub preamble: Preamble<Address, Signature, Extension>,
247 pub function: Call,
249}
250
251impl<Address, Call, Signature, Extension> TypeInfo
256 for UncheckedExtrinsic<Address, Call, Signature, Extension>
257where
258 Address: StaticTypeInfo,
259 Call: StaticTypeInfo,
260 Signature: StaticTypeInfo,
261 Extension: StaticTypeInfo,
262{
263 type Identity = UncheckedExtrinsic<Address, Call, Signature, Extension>;
264
265 fn type_info() -> Type {
266 Type::builder()
267 .path(Path::new("UncheckedExtrinsic", module_path!()))
268 .type_params(vec![
272 TypeParameter::new("Address", Some(meta_type::<Address>())),
273 TypeParameter::new("Call", Some(meta_type::<Call>())),
274 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
275 TypeParameter::new("Extra", Some(meta_type::<Extension>())),
276 ])
277 .docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"])
278 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
282 }
283}
284
285impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize>
286 UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
287{
288 #[deprecated = "Use new_bare instead"]
292 pub fn new_unsigned(function: Call) -> Self {
293 Self::new_bare(function)
294 }
295
296 pub fn is_inherent(&self) -> bool {
298 matches!(self.preamble, Preamble::Bare(_))
299 }
300
301 pub fn is_signed(&self) -> bool {
304 matches!(self.preamble, Preamble::Signed(..))
305 }
306
307 pub fn from_parts(function: Call, preamble: Preamble<Address, Signature, Extension>) -> Self {
309 Self { preamble, function }
310 }
311
312 pub fn new_bare(function: Call) -> Self {
314 Self::from_parts(function, Preamble::Bare(EXTRINSIC_FORMAT_VERSION))
315 }
316
317 pub fn new_bare_legacy(function: Call) -> Self {
319 Self::from_parts(function, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION))
320 }
321
322 pub fn new_signed(
324 function: Call,
325 signed: Address,
326 signature: Signature,
327 tx_ext: Extension,
328 ) -> Self {
329 Self::from_parts(function, Preamble::Signed(signed, signature, tx_ext))
330 }
331
332 pub fn new_transaction(function: Call, tx_ext: Extension) -> Self {
334 Self::from_parts(function, Preamble::General(EXTENSION_VERSION, tx_ext))
335 }
336
337 fn decode_with_len<I: Input>(input: &mut I, len: usize) -> Result<Self, codec::Error>
338 where
339 Preamble<Address, Signature, Extension>: Decode,
340 Call: DecodeWithMemTracking,
341 {
342 let mut input = CountedInput::new(input);
343
344 let preamble = Decode::decode(&mut input)?;
345 let function = Call::decode_with_mem_limit(&mut input, MAX_CALL_SIZE.saturating_add(1))?;
348
349 if input.count() != len as u64 {
350 return Err("Invalid length prefix".into());
351 }
352
353 Ok(Self { preamble, function })
354 }
355
356 fn encode_without_prefix(&self) -> Vec<u8>
357 where
358 Preamble<Address, Signature, Extension>: Encode,
359 Call: Encode,
360 {
361 (&self.preamble, &self.function).encode()
362 }
363}
364
365impl<Address, Call, Signature, Extension> ExtrinsicLike
366 for UncheckedExtrinsic<Address, Call, Signature, Extension>
367{
368 fn is_signed(&self) -> Option<bool> {
369 Some(matches!(self.preamble, Preamble::Signed(..)))
370 }
371
372 fn is_bare(&self) -> bool {
373 matches!(self.preamble, Preamble::Bare(_))
374 }
375}
376
377impl<Address, Call, Signature, Extra> ExtrinsicCall
378 for UncheckedExtrinsic<Address, Call, Signature, Extra>
379{
380 type Call = Call;
381
382 fn call(&self) -> &Call {
383 &self.function
384 }
385
386 fn into_call(self) -> Self::Call {
387 self.function
388 }
389}
390
391impl<LookupSource, AccountId, Call, Signature, Extension, Lookup> Checkable<Lookup>
396 for UncheckedExtrinsic<LookupSource, Call, Signature, Extension>
397where
398 LookupSource: Member + MaybeDisplay,
399 Call: Encode + Member + Dispatchable,
400 Signature: Member + traits::Verify,
401 <Signature as traits::Verify>::Signer: IdentifyAccount<AccountId = AccountId>,
402 Extension: Encode + TransactionExtension<Call>,
403 AccountId: Member + MaybeDisplay,
404 Lookup: traits::Lookup<Source = LookupSource, Target = AccountId>,
405{
406 type Checked = CheckedExtrinsic<AccountId, Call, Extension>;
407
408 fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
409 Ok(match self.preamble {
410 Preamble::Signed(signed, signature, tx_ext) => {
411 let signed = lookup.lookup(signed)?;
412 let raw_payload = SignedPayload::new(self.function, tx_ext)?;
414 if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
415 return Err(InvalidTransaction::BadProof.into());
416 }
417 let (function, tx_ext, _) = raw_payload.deconstruct();
418 CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function }
419 },
420 Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
421 format: ExtrinsicFormat::General(extension_version, tx_ext),
422 function: self.function,
423 },
424 Preamble::Bare(_) => {
425 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }
426 },
427 })
428 }
429
430 #[cfg(feature = "try-runtime")]
431 fn unchecked_into_checked_i_know_what_i_am_doing(
432 self,
433 lookup: &Lookup,
434 ) -> Result<Self::Checked, TransactionValidityError> {
435 Ok(match self.preamble {
436 Preamble::Signed(signed, _, tx_ext) => {
437 let signed = lookup.lookup(signed)?;
438 CheckedExtrinsic {
439 format: ExtrinsicFormat::Signed(signed, tx_ext),
440 function: self.function,
441 }
442 },
443 Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
444 format: ExtrinsicFormat::General(extension_version, tx_ext),
445 function: self.function,
446 },
447 Preamble::Bare(_) => {
448 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }
449 },
450 })
451 }
452}
453
454impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
455 ExtrinsicMetadata for UncheckedExtrinsic<Address, Call, Signature, Extension>
456{
457 const VERSIONS: &'static [u8] = &[LEGACY_EXTRINSIC_FORMAT_VERSION, EXTRINSIC_FORMAT_VERSION];
458 type TransactionExtensions = Extension;
459}
460
461impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
462 UncheckedExtrinsic<Address, Call, Signature, Extension>
463{
464 pub fn extension_weight(&self) -> Weight {
467 match &self.preamble {
468 Preamble::Bare(_) => Weight::zero(),
469 Preamble::Signed(_, _, ext) | Preamble::General(_, ext) => ext.weight(&self.function),
470 }
471 }
472}
473
474impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize> Decode
475 for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
476where
477 Address: Decode,
478 Signature: Decode,
479 Call: DecodeWithMemTracking,
480 Extension: Decode,
481{
482 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
483 let expected_length: Compact<u32> = Decode::decode(input)?;
487
488 Self::decode_with_len(input, expected_length.0 as usize)
489 }
490}
491
492#[docify::export(unchecked_extrinsic_encode_impl)]
493impl<Address, Call, Signature, Extension> Encode
494 for UncheckedExtrinsic<Address, Call, Signature, Extension>
495where
496 Preamble<Address, Signature, Extension>: Encode,
497 Call: Encode,
498 Extension: Encode,
499{
500 fn encode(&self) -> Vec<u8> {
501 let tmp = self.encode_without_prefix();
502
503 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
504
505 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
507
508 compact_len.encode_to(&mut output);
509 output.extend(tmp);
510
511 output
512 }
513}
514
515impl<Address, Call, Signature, Extension> EncodeLike
516 for UncheckedExtrinsic<Address, Call, Signature, Extension>
517where
518 Address: Encode,
519 Signature: Encode,
520 Call: Encode + Dispatchable,
521 Extension: TransactionExtension<Call>,
522{
523}
524
525#[cfg(feature = "serde")]
526impl<Address: Encode, Signature: Encode, Call: Encode, Extension: Encode> serde::Serialize
527 for UncheckedExtrinsic<Address, Call, Signature, Extension>
528{
529 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
530 where
531 S: ::serde::Serializer,
532 {
533 self.using_encoded(|bytes| seq.serialize_bytes(bytes))
534 }
535}
536
537#[cfg(feature = "serde")]
538impl<'a, Address: Decode, Signature: Decode, Call: DecodeWithMemTracking, Extension: Decode>
539 serde::Deserialize<'a> for UncheckedExtrinsic<Address, Call, Signature, Extension>
540{
541 fn deserialize<D>(de: D) -> Result<Self, D::Error>
542 where
543 D: serde::Deserializer<'a>,
544 {
545 let r = pezsp_core::bytes::deserialize(de)?;
546 Self::decode(&mut &r[..])
547 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
548 }
549}
550
551pub struct SignedPayload<Call: Dispatchable, Extension: TransactionExtension<Call>>(
557 (Call, Extension, Extension::Implicit),
558);
559
560impl<Call, Extension> SignedPayload<Call, Extension>
561where
562 Call: Encode + Dispatchable,
563 Extension: TransactionExtension<Call>,
564{
565 pub fn new(call: Call, tx_ext: Extension) -> Result<Self, TransactionValidityError> {
569 let implicit = Extension::implicit(&tx_ext)?;
570 let raw_payload = (call, tx_ext, implicit);
571 Ok(Self(raw_payload))
572 }
573
574 pub fn from_raw(call: Call, tx_ext: Extension, implicit: Extension::Implicit) -> Self {
576 Self((call, tx_ext, implicit))
577 }
578
579 pub fn deconstruct(self) -> (Call, Extension, Extension::Implicit) {
581 self.0
582 }
583}
584
585impl<Call, Extension> Encode for SignedPayload<Call, Extension>
586where
587 Call: Encode + Dispatchable,
588 Extension: TransactionExtension<Call>,
589{
590 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
592 self.0.using_encoded(|payload| {
593 if payload.len() > 256 {
594 f(&blake2_256(payload)[..])
595 } else {
596 f(payload)
597 }
598 })
599 }
600}
601
602impl<Call, Extension> EncodeLike for SignedPayload<Call, Extension>
603where
604 Call: Encode + Dispatchable,
605 Extension: TransactionExtension<Call>,
606{
607}
608
609impl<Address, Call, Signature, Extension>
610 From<UncheckedExtrinsic<Address, Call, Signature, Extension>> for OpaqueExtrinsic
611where
612 Preamble<Address, Signature, Extension>: Encode,
613 Call: Encode,
614{
615 fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extension>) -> Self {
616 Self::from_blob(extrinsic.encode_without_prefix())
617 }
618}
619
620impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize> LazyExtrinsic
621 for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
622where
623 Preamble<Address, Signature, Extension>: Decode,
624 Call: DecodeWithMemTracking,
625{
626 fn decode_unprefixed(data: &[u8]) -> Result<Self, codec::Error> {
627 Self::decode_with_len(&mut &data[..], data.len())
628 }
629}
630
631#[cfg(test)]
632mod legacy {
633 use codec::{Compact, Decode, Encode, EncodeLike, Error, Input};
634 use scale_info::{
635 build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter,
636 };
637
638 pub type UncheckedSignaturePayloadV4<Address, Signature, Extra> = (Address, Signature, Extra);
639
640 #[derive(PartialEq, Eq, Clone, Debug)]
641 pub struct UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
642 pub signature: Option<UncheckedSignaturePayloadV4<Address, Signature, Extra>>,
643 pub function: Call,
644 }
645
646 impl<Address, Call, Signature, Extra> TypeInfo
647 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
648 where
649 Address: StaticTypeInfo,
650 Call: StaticTypeInfo,
651 Signature: StaticTypeInfo,
652 Extra: StaticTypeInfo,
653 {
654 type Identity = UncheckedExtrinsicV4<Address, Call, Signature, Extra>;
655
656 fn type_info() -> Type {
657 Type::builder()
658 .path(Path::new("UncheckedExtrinsic", module_path!()))
659 .type_params(vec![
664 TypeParameter::new("Address", Some(meta_type::<Address>())),
665 TypeParameter::new("Call", Some(meta_type::<Call>())),
666 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
667 TypeParameter::new("Extra", Some(meta_type::<Extra>())),
668 ])
669 .docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"])
670 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
674 }
675 }
676
677 impl<Address, Call, Signature, Extra> UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
678 pub fn new_signed(
679 function: Call,
680 signed: Address,
681 signature: Signature,
682 extra: Extra,
683 ) -> Self {
684 Self { signature: Some((signed, signature, extra)), function }
685 }
686
687 pub fn new_unsigned(function: Call) -> Self {
688 Self { signature: None, function }
689 }
690 }
691
692 impl<Address, Call, Signature, Extra> Decode
693 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
694 where
695 Address: Decode,
696 Signature: Decode,
697 Call: Decode,
698 Extra: Decode,
699 {
700 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
701 let expected_length: Compact<u32> = Decode::decode(input)?;
705 let before_length = input.remaining_len()?;
706
707 let version = input.read_byte()?;
708
709 let is_signed = version & 0b1000_0000 != 0;
710 let version = version & 0b0111_1111;
711 if version != 4u8 {
712 return Err("Invalid transaction version".into());
713 }
714
715 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
716 let function = Decode::decode(input)?;
717
718 if let Some((before_length, after_length)) =
719 input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
720 {
721 let length = before_length.saturating_sub(after_length);
722
723 if length != expected_length.0 as usize {
724 return Err("Invalid length prefix".into());
725 }
726 }
727
728 Ok(Self { signature, function })
729 }
730 }
731
732 #[docify::export(unchecked_extrinsic_encode_impl)]
733 impl<Address, Call, Signature, Extra> Encode
734 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
735 where
736 Address: Encode,
737 Signature: Encode,
738 Call: Encode,
739 Extra: Encode,
740 {
741 fn encode(&self) -> Vec<u8> {
742 let mut tmp = Vec::with_capacity(core::mem::size_of::<Self>());
743
744 match self.signature.as_ref() {
746 Some(s) => {
747 tmp.push(4u8 | 0b1000_0000);
748 s.encode_to(&mut tmp);
749 },
750 None => {
751 tmp.push(4u8 & 0b0111_1111);
752 },
753 }
754 self.function.encode_to(&mut tmp);
755
756 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
757
758 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
760
761 compact_len.encode_to(&mut output);
762 output.extend(tmp);
763
764 output
765 }
766 }
767
768 impl<Address, Call, Signature, Extra> EncodeLike
769 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
770 where
771 Address: Encode,
772 Signature: Encode,
773 Call: Encode,
774 Extra: Encode,
775 {
776 }
777}
778
779#[cfg(test)]
780mod tests {
781 use super::{legacy::UncheckedExtrinsicV4, *};
782 use crate::{
783 codec::{Decode, Encode},
784 impl_tx_ext_default,
785 testing::TestSignature as TestSig,
786 traits::{FakeDispatchable, IdentityLookup, TransactionExtension},
787 };
788 use pezsp_io::hashing::blake2_256;
789
790 type TestContext = IdentityLookup<u64>;
791 type TestAccountId = u64;
792 type TestCall = FakeDispatchable<Vec<u8>>;
793
794 const TEST_ACCOUNT: TestAccountId = 0;
795
796 #[derive(
798 Debug,
799 Encode,
800 Decode,
801 DecodeWithMemTracking,
802 Clone,
803 Eq,
804 PartialEq,
805 Ord,
806 PartialOrd,
807 TypeInfo,
808 )]
809 struct DummyExtension;
810 impl TransactionExtension<TestCall> for DummyExtension {
811 const IDENTIFIER: &'static str = "DummyExtension";
812 type Implicit = ();
813 type Val = ();
814 type Pre = ();
815 impl_tx_ext_default!(TestCall; weight validate prepare);
816 }
817
818 type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, DummyExtension>;
819 type CEx = CheckedExtrinsic<TestAccountId, TestCall, DummyExtension>;
820
821 #[test]
822 fn unsigned_codec_should_work() {
823 let call: TestCall = vec![0u8; 0].into();
824 let ux = Ex::new_bare(call);
825 let encoded = ux.encode();
826 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
827 }
828
829 #[test]
830 fn invalid_length_prefix_is_detected() {
831 let ux = Ex::new_bare(vec![0u8; 0].into());
832 let mut encoded = ux.encode();
833
834 let length = Compact::<u32>::decode(&mut &encoded[..]).unwrap();
835 Compact(length.0 + 10).encode_to(&mut &mut encoded[..1]);
836
837 assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into()));
838 }
839
840 #[test]
841 fn transaction_codec_should_work() {
842 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
843 let encoded = ux.encode();
844 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
845 }
846
847 #[test]
848 fn signed_codec_should_work() {
849 let ux = Ex::new_signed(
850 vec![0u8; 0].into(),
851 TEST_ACCOUNT,
852 TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()),
853 DummyExtension,
854 );
855 let encoded = ux.encode();
856 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
857 }
858
859 #[test]
860 fn large_signed_codec_should_work() {
861 let ux = Ex::new_signed(
862 vec![0u8; 0].into(),
863 TEST_ACCOUNT,
864 TestSig(
865 TEST_ACCOUNT,
866 (vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(),
867 ),
868 DummyExtension,
869 );
870 let encoded = ux.encode();
871 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
872 }
873
874 #[test]
875 fn unsigned_check_should_work() {
876 let ux = Ex::new_bare(vec![0u8; 0].into());
877 assert!(ux.is_inherent());
878 assert_eq!(
879 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
880 Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }),
881 );
882 }
883
884 #[test]
885 fn badly_signed_check_should_fail() {
886 let ux = Ex::new_signed(
887 vec![0u8; 0].into(),
888 TEST_ACCOUNT,
889 TestSig(TEST_ACCOUNT, vec![0u8; 0].into()),
890 DummyExtension,
891 );
892 assert!(!ux.is_inherent());
893 assert_eq!(
894 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
895 Err(InvalidTransaction::BadProof.into()),
896 );
897 }
898
899 #[test]
900 fn transaction_check_should_work() {
901 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
902 assert!(!ux.is_inherent());
903 assert_eq!(
904 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
905 Ok(CEx {
906 format: ExtrinsicFormat::General(0, DummyExtension),
907 function: vec![0u8; 0].into()
908 }),
909 );
910 }
911
912 #[test]
913 fn signed_check_should_work() {
914 let sig_payload = SignedPayload::from_raw(
915 FakeDispatchable::from(vec![0u8; 0]),
916 DummyExtension,
917 DummyExtension.implicit().unwrap(),
918 );
919 let ux = Ex::new_signed(
920 vec![0u8; 0].into(),
921 TEST_ACCOUNT,
922 TestSig(TEST_ACCOUNT, sig_payload.encode()),
923 DummyExtension,
924 );
925 assert!(!ux.is_inherent());
926 assert_eq!(
927 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
928 Ok(CEx {
929 format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension),
930 function: vec![0u8; 0].into()
931 }),
932 );
933 }
934
935 #[test]
936 fn encoding_matches_vec() {
937 let ex = Ex::new_bare(vec![0u8; 0].into());
938 let encoded = ex.encode();
939 let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
940 assert_eq!(decoded, ex);
941 let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
942 assert_eq!(as_vec.encode(), encoded);
943 }
944
945 #[test]
946 fn conversion_to_opaque() {
947 let ux = Ex::new_bare(vec![0u8; 0].into());
948 let encoded = ux.encode();
949 let opaque: OpaqueExtrinsic = ux.into();
950 let opaque_encoded = opaque.encode();
951 assert_eq!(opaque_encoded, encoded);
952 }
953
954 #[test]
955 fn large_bad_prefix_should_work() {
956 let encoded = (Compact::<u32>::from(u32::MAX), Preamble::<(), (), ()>::Bare(0)).encode();
957 assert!(Ex::decode(&mut &encoded[..]).is_err());
958 }
959
960 #[test]
961 fn legacy_short_signed_encode_decode() {
962 let call: TestCall = vec![0u8; 4].into();
963 let signed = TEST_ACCOUNT;
964 let extension = DummyExtension;
965 let implicit = extension.implicit().unwrap();
966 let legacy_signature = TestSig(TEST_ACCOUNT, (&call, &extension, &implicit).encode());
967
968 let old_ux =
969 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
970 call.clone(),
971 signed,
972 legacy_signature.clone(),
973 extension.clone(),
974 );
975
976 let encoded_old_ux = old_ux.encode();
977 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
978
979 assert_eq!(decoded_old_ux.function, call);
980 assert_eq!(
981 decoded_old_ux.preamble,
982 Preamble::Signed(signed, legacy_signature.clone(), extension.clone())
983 );
984
985 let new_ux =
986 Ex::new_signed(call.clone(), signed, legacy_signature.clone(), extension.clone());
987
988 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
989 let old_checked =
990 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
991 assert_eq!(new_checked, old_checked);
992 }
993
994 #[test]
995 fn legacy_long_signed_encode_decode() {
996 let call: TestCall = vec![0u8; 257].into();
997 let signed = TEST_ACCOUNT;
998 let extension = DummyExtension;
999 let implicit = extension.implicit().unwrap();
1000 let signature = TestSig(
1001 TEST_ACCOUNT,
1002 blake2_256(&(&call, DummyExtension, &implicit).encode()[..]).to_vec(),
1003 );
1004
1005 let old_ux =
1006 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
1007 call.clone(),
1008 signed,
1009 signature.clone(),
1010 extension.clone(),
1011 );
1012
1013 let encoded_old_ux = old_ux.encode();
1014 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1015
1016 assert_eq!(decoded_old_ux.function, call);
1017 assert_eq!(
1018 decoded_old_ux.preamble,
1019 Preamble::Signed(signed, signature.clone(), extension.clone())
1020 );
1021
1022 let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone());
1023
1024 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1025 let old_checked =
1026 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1027 assert_eq!(new_checked, old_checked);
1028 }
1029
1030 #[test]
1031 fn legacy_unsigned_encode_decode() {
1032 let call: TestCall = vec![0u8; 0].into();
1033
1034 let old_ux =
1035 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_unsigned(
1036 call.clone(),
1037 );
1038
1039 let encoded_old_ux = old_ux.encode();
1040 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1041
1042 assert_eq!(decoded_old_ux.function, call);
1043 assert_eq!(decoded_old_ux.preamble, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION));
1044
1045 let new_legacy_ux = Ex::new_bare_legacy(call.clone());
1046 assert_eq!(encoded_old_ux, new_legacy_ux.encode());
1047
1048 let new_ux = Ex::new_bare(call.clone());
1049 let encoded_new_ux = new_ux.encode();
1050 let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap();
1051 assert_eq!(new_ux, decoded_new_ux);
1052
1053 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1054 let old_checked =
1055 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1056 assert_eq!(new_checked, old_checked);
1057 }
1058
1059 #[test]
1060 fn max_call_heap_size_should_be_checked() {
1061 let ux = Ex::new_bare(vec![0u8; DEFAULT_MAX_CALL_SIZE].into());
1064 let encoded = ux.encode();
1065 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1066
1067 let ux = Ex::new_bare(vec![0u8; DEFAULT_MAX_CALL_SIZE + 1].into());
1069 let encoded = ux.encode();
1070 assert_eq!(
1071 Ex::decode(&mut &encoded[..]).unwrap_err().to_string(),
1072 "Could not decode `FakeDispatchable.0`:\n\tHeap memory limit exceeded while decoding\n"
1073 );
1074 }
1075}