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