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