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, DecodeWithMemTracking, 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(DecodeWithMemTracking, 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(DecodeWithMemTracking, PartialEq, Eq, Clone, Debug)]
223#[codec(decode_with_mem_tracking_bound(
224 Address: DecodeWithMemTracking,
225 Call: DecodeWithMemTracking,
226 Signature: DecodeWithMemTracking,
227 Extension: DecodeWithMemTracking)
228)]
229pub struct UncheckedExtrinsic<Address, Call, Signature, Extension> {
230 pub preamble: Preamble<Address, Signature, Extension>,
233 pub function: Call,
235}
236
237impl<Address, Call, Signature, Extension> TypeInfo
242 for UncheckedExtrinsic<Address, Call, Signature, Extension>
243where
244 Address: StaticTypeInfo,
245 Call: StaticTypeInfo,
246 Signature: StaticTypeInfo,
247 Extension: StaticTypeInfo,
248{
249 type Identity = UncheckedExtrinsic<Address, Call, Signature, Extension>;
250
251 fn type_info() -> Type {
252 Type::builder()
253 .path(Path::new("UncheckedExtrinsic", module_path!()))
254 .type_params(vec![
258 TypeParameter::new("Address", Some(meta_type::<Address>())),
259 TypeParameter::new("Call", Some(meta_type::<Call>())),
260 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
261 TypeParameter::new("Extra", Some(meta_type::<Extension>())),
262 ])
263 .docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"])
264 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
268 }
269}
270
271impl<Address, Call, Signature, Extension> UncheckedExtrinsic<Address, Call, Signature, Extension> {
272 #[deprecated = "Use new_bare instead"]
276 pub fn new_unsigned(function: Call) -> Self {
277 Self::new_bare(function)
278 }
279
280 pub fn is_inherent(&self) -> bool {
282 matches!(self.preamble, Preamble::Bare(_))
283 }
284
285 pub fn is_signed(&self) -> bool {
288 matches!(self.preamble, Preamble::Signed(..))
289 }
290
291 pub fn from_parts(function: Call, preamble: Preamble<Address, Signature, Extension>) -> Self {
293 Self { preamble, function }
294 }
295
296 pub fn new_bare(function: Call) -> Self {
298 Self { preamble: Preamble::Bare(EXTRINSIC_FORMAT_VERSION), function }
299 }
300
301 pub fn new_bare_legacy(function: Call) -> Self {
303 Self { preamble: Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION), function }
304 }
305
306 pub fn new_signed(
308 function: Call,
309 signed: Address,
310 signature: Signature,
311 tx_ext: Extension,
312 ) -> Self {
313 Self { preamble: Preamble::Signed(signed, signature, tx_ext), function }
314 }
315
316 pub fn new_transaction(function: Call, tx_ext: Extension) -> Self {
318 Self { preamble: Preamble::General(EXTENSION_VERSION, tx_ext), function }
319 }
320}
321
322impl<Address: TypeInfo, Call: TypeInfo, Signature: TypeInfo, Extension: TypeInfo> ExtrinsicLike
323 for UncheckedExtrinsic<Address, Call, Signature, Extension>
324{
325 fn is_bare(&self) -> bool {
326 matches!(self.preamble, Preamble::Bare(_))
327 }
328
329 fn is_signed(&self) -> Option<bool> {
330 Some(matches!(self.preamble, Preamble::Signed(..)))
331 }
332}
333
334impl<LookupSource, AccountId, Call, Signature, Extension, Lookup> Checkable<Lookup>
339 for UncheckedExtrinsic<LookupSource, Call, Signature, Extension>
340where
341 LookupSource: Member + MaybeDisplay,
342 Call: Encode + Member + Dispatchable,
343 Signature: Member + traits::Verify,
344 <Signature as traits::Verify>::Signer: IdentifyAccount<AccountId = AccountId>,
345 Extension: Encode + TransactionExtension<Call>,
346 AccountId: Member + MaybeDisplay,
347 Lookup: traits::Lookup<Source = LookupSource, Target = AccountId>,
348{
349 type Checked = CheckedExtrinsic<AccountId, Call, Extension>;
350
351 fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
352 Ok(match self.preamble {
353 Preamble::Signed(signed, signature, tx_ext) => {
354 let signed = lookup.lookup(signed)?;
355 let raw_payload = SignedPayload::new(self.function, tx_ext)?;
357 if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
358 return Err(InvalidTransaction::BadProof.into())
359 }
360 let (function, tx_ext, _) = raw_payload.deconstruct();
361 CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function }
362 },
363 Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
364 format: ExtrinsicFormat::General(extension_version, tx_ext),
365 function: self.function,
366 },
367 Preamble::Bare(_) =>
368 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function },
369 })
370 }
371
372 #[cfg(feature = "try-runtime")]
373 fn unchecked_into_checked_i_know_what_i_am_doing(
374 self,
375 lookup: &Lookup,
376 ) -> Result<Self::Checked, TransactionValidityError> {
377 Ok(match self.preamble {
378 Preamble::Signed(signed, _, tx_ext) => {
379 let signed = lookup.lookup(signed)?;
380 CheckedExtrinsic {
381 format: ExtrinsicFormat::Signed(signed, tx_ext),
382 function: self.function,
383 }
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
395impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
396 ExtrinsicMetadata for UncheckedExtrinsic<Address, Call, Signature, Extension>
397{
398 const VERSIONS: &'static [u8] = &[LEGACY_EXTRINSIC_FORMAT_VERSION, EXTRINSIC_FORMAT_VERSION];
399 type TransactionExtensions = Extension;
400}
401
402impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
403 UncheckedExtrinsic<Address, Call, Signature, Extension>
404{
405 pub fn extension_weight(&self) -> Weight {
408 match &self.preamble {
409 Preamble::Bare(_) => Weight::zero(),
410 Preamble::Signed(_, _, ext) | Preamble::General(_, ext) => ext.weight(&self.function),
411 }
412 }
413}
414
415impl<Address, Call, Signature, Extension> Decode
416 for UncheckedExtrinsic<Address, Call, Signature, Extension>
417where
418 Address: Decode,
419 Signature: Decode,
420 Call: Decode,
421 Extension: Decode,
422{
423 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
424 let expected_length: Compact<u32> = Decode::decode(input)?;
428 let before_length = input.remaining_len()?;
429
430 let preamble = Decode::decode(input)?;
431 let function = Decode::decode(input)?;
432
433 if let Some((before_length, after_length)) =
434 input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
435 {
436 let length = before_length.saturating_sub(after_length);
437
438 if length != expected_length.0 as usize {
439 return Err("Invalid length prefix".into())
440 }
441 }
442
443 Ok(Self { preamble, function })
444 }
445}
446
447#[docify::export(unchecked_extrinsic_encode_impl)]
448impl<Address, Call, Signature, Extension> Encode
449 for UncheckedExtrinsic<Address, Call, Signature, Extension>
450where
451 Preamble<Address, Signature, Extension>: Encode,
452 Call: Encode,
453 Extension: Encode,
454{
455 fn encode(&self) -> Vec<u8> {
456 let mut tmp = self.preamble.encode();
457 self.function.encode_to(&mut tmp);
458
459 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
460
461 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
463
464 compact_len.encode_to(&mut output);
465 output.extend(tmp);
466
467 output
468 }
469}
470
471impl<Address, Call, Signature, Extension> EncodeLike
472 for UncheckedExtrinsic<Address, Call, Signature, Extension>
473where
474 Address: Encode,
475 Signature: Encode,
476 Call: Encode + Dispatchable,
477 Extension: TransactionExtension<Call>,
478{
479}
480
481#[cfg(feature = "serde")]
482impl<Address: Encode, Signature: Encode, Call: Encode, Extension: Encode> serde::Serialize
483 for UncheckedExtrinsic<Address, Call, Signature, Extension>
484{
485 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
486 where
487 S: ::serde::Serializer,
488 {
489 self.using_encoded(|bytes| seq.serialize_bytes(bytes))
490 }
491}
492
493#[cfg(feature = "serde")]
494impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extension: Decode> serde::Deserialize<'a>
495 for UncheckedExtrinsic<Address, Call, Signature, Extension>
496{
497 fn deserialize<D>(de: D) -> Result<Self, D::Error>
498 where
499 D: serde::Deserializer<'a>,
500 {
501 let r = sp_core::bytes::deserialize(de)?;
502 Self::decode(&mut &r[..])
503 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
504 }
505}
506
507pub struct SignedPayload<Call: Dispatchable, Extension: TransactionExtension<Call>>(
513 (Call, Extension, Extension::Implicit),
514);
515
516impl<Call, Extension> SignedPayload<Call, Extension>
517where
518 Call: Encode + Dispatchable,
519 Extension: TransactionExtension<Call>,
520{
521 pub fn new(call: Call, tx_ext: Extension) -> Result<Self, TransactionValidityError> {
525 let implicit = Extension::implicit(&tx_ext)?;
526 let raw_payload = (call, tx_ext, implicit);
527 Ok(Self(raw_payload))
528 }
529
530 pub fn from_raw(call: Call, tx_ext: Extension, implicit: Extension::Implicit) -> Self {
532 Self((call, tx_ext, implicit))
533 }
534
535 pub fn deconstruct(self) -> (Call, Extension, Extension::Implicit) {
537 self.0
538 }
539}
540
541impl<Call, Extension> Encode for SignedPayload<Call, Extension>
542where
543 Call: Encode + Dispatchable,
544 Extension: TransactionExtension<Call>,
545{
546 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
548 self.0.using_encoded(|payload| {
549 if payload.len() > 256 {
550 f(&blake2_256(payload)[..])
551 } else {
552 f(payload)
553 }
554 })
555 }
556}
557
558impl<Call, Extension> EncodeLike for SignedPayload<Call, Extension>
559where
560 Call: Encode + Dispatchable,
561 Extension: TransactionExtension<Call>,
562{
563}
564
565impl<Address, Call, Signature, Extension>
566 From<UncheckedExtrinsic<Address, Call, Signature, Extension>> for OpaqueExtrinsic
567where
568 Address: Encode,
569 Signature: Encode,
570 Call: Encode,
571 Extension: Encode,
572{
573 fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extension>) -> Self {
574 Self::from_bytes(extrinsic.encode().as_slice()).expect(
575 "both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \
576 raw Vec<u8> encoding; qed",
577 )
578 }
579}
580
581#[cfg(test)]
582mod legacy {
583 use codec::{Compact, Decode, Encode, EncodeLike, Error, Input};
584 use scale_info::{
585 build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter,
586 };
587
588 pub type UncheckedSignaturePayloadV4<Address, Signature, Extra> = (Address, Signature, Extra);
589
590 #[derive(PartialEq, Eq, Clone, Debug)]
591 pub struct UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
592 pub signature: Option<UncheckedSignaturePayloadV4<Address, Signature, Extra>>,
593 pub function: Call,
594 }
595
596 impl<Address, Call, Signature, Extra> TypeInfo
597 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
598 where
599 Address: StaticTypeInfo,
600 Call: StaticTypeInfo,
601 Signature: StaticTypeInfo,
602 Extra: StaticTypeInfo,
603 {
604 type Identity = UncheckedExtrinsicV4<Address, Call, Signature, Extra>;
605
606 fn type_info() -> Type {
607 Type::builder()
608 .path(Path::new("UncheckedExtrinsic", module_path!()))
609 .type_params(vec![
614 TypeParameter::new("Address", Some(meta_type::<Address>())),
615 TypeParameter::new("Call", Some(meta_type::<Call>())),
616 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
617 TypeParameter::new("Extra", Some(meta_type::<Extra>())),
618 ])
619 .docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"])
620 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
624 }
625 }
626
627 impl<Address, Call, Signature, Extra> UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
628 pub fn new_signed(
629 function: Call,
630 signed: Address,
631 signature: Signature,
632 extra: Extra,
633 ) -> Self {
634 Self { signature: Some((signed, signature, extra)), function }
635 }
636
637 pub fn new_unsigned(function: Call) -> Self {
638 Self { signature: None, function }
639 }
640 }
641
642 impl<Address, Call, Signature, Extra> Decode
643 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
644 where
645 Address: Decode,
646 Signature: Decode,
647 Call: Decode,
648 Extra: Decode,
649 {
650 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
651 let expected_length: Compact<u32> = Decode::decode(input)?;
655 let before_length = input.remaining_len()?;
656
657 let version = input.read_byte()?;
658
659 let is_signed = version & 0b1000_0000 != 0;
660 let version = version & 0b0111_1111;
661 if version != 4u8 {
662 return Err("Invalid transaction version".into())
663 }
664
665 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
666 let function = Decode::decode(input)?;
667
668 if let Some((before_length, after_length)) =
669 input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
670 {
671 let length = before_length.saturating_sub(after_length);
672
673 if length != expected_length.0 as usize {
674 return Err("Invalid length prefix".into())
675 }
676 }
677
678 Ok(Self { signature, function })
679 }
680 }
681
682 #[docify::export(unchecked_extrinsic_encode_impl)]
683 impl<Address, Call, Signature, Extra> Encode
684 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
685 where
686 Address: Encode,
687 Signature: Encode,
688 Call: Encode,
689 Extra: Encode,
690 {
691 fn encode(&self) -> Vec<u8> {
692 let mut tmp = Vec::with_capacity(core::mem::size_of::<Self>());
693
694 match self.signature.as_ref() {
696 Some(s) => {
697 tmp.push(4u8 | 0b1000_0000);
698 s.encode_to(&mut tmp);
699 },
700 None => {
701 tmp.push(4u8 & 0b0111_1111);
702 },
703 }
704 self.function.encode_to(&mut tmp);
705
706 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
707
708 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
710
711 compact_len.encode_to(&mut output);
712 output.extend(tmp);
713
714 output
715 }
716 }
717
718 impl<Address, Call, Signature, Extra> EncodeLike
719 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
720 where
721 Address: Encode,
722 Signature: Encode,
723 Call: Encode,
724 Extra: Encode,
725 {
726 }
727}
728
729#[cfg(test)]
730mod tests {
731 use super::{legacy::UncheckedExtrinsicV4, *};
732 use crate::{
733 codec::{Decode, Encode},
734 impl_tx_ext_default,
735 testing::TestSignature as TestSig,
736 traits::{FakeDispatchable, IdentityLookup, TransactionExtension},
737 };
738 use sp_io::hashing::blake2_256;
739
740 type TestContext = IdentityLookup<u64>;
741 type TestAccountId = u64;
742 type TestCall = FakeDispatchable<Vec<u8>>;
743
744 const TEST_ACCOUNT: TestAccountId = 0;
745
746 #[derive(
748 Debug,
749 Encode,
750 Decode,
751 DecodeWithMemTracking,
752 Clone,
753 Eq,
754 PartialEq,
755 Ord,
756 PartialOrd,
757 TypeInfo,
758 )]
759 struct DummyExtension;
760 impl TransactionExtension<TestCall> for DummyExtension {
761 const IDENTIFIER: &'static str = "DummyExtension";
762 type Implicit = ();
763 type Val = ();
764 type Pre = ();
765 impl_tx_ext_default!(TestCall; weight validate prepare);
766 }
767
768 type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, DummyExtension>;
769 type CEx = CheckedExtrinsic<TestAccountId, TestCall, DummyExtension>;
770
771 #[test]
772 fn unsigned_codec_should_work() {
773 let call: TestCall = vec![0u8; 0].into();
774 let ux = Ex::new_bare(call);
775 let encoded = ux.encode();
776 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
777 }
778
779 #[test]
780 fn invalid_length_prefix_is_detected() {
781 let ux = Ex::new_bare(vec![0u8; 0].into());
782 let mut encoded = ux.encode();
783
784 let length = Compact::<u32>::decode(&mut &encoded[..]).unwrap();
785 Compact(length.0 + 10).encode_to(&mut &mut encoded[..1]);
786
787 assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into()));
788 }
789
790 #[test]
791 fn transaction_codec_should_work() {
792 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
793 let encoded = ux.encode();
794 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
795 }
796
797 #[test]
798 fn signed_codec_should_work() {
799 let ux = Ex::new_signed(
800 vec![0u8; 0].into(),
801 TEST_ACCOUNT,
802 TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()),
803 DummyExtension,
804 );
805 let encoded = ux.encode();
806 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
807 }
808
809 #[test]
810 fn large_signed_codec_should_work() {
811 let ux = Ex::new_signed(
812 vec![0u8; 0].into(),
813 TEST_ACCOUNT,
814 TestSig(
815 TEST_ACCOUNT,
816 (vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(),
817 ),
818 DummyExtension,
819 );
820 let encoded = ux.encode();
821 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
822 }
823
824 #[test]
825 fn unsigned_check_should_work() {
826 let ux = Ex::new_bare(vec![0u8; 0].into());
827 assert!(ux.is_inherent());
828 assert_eq!(
829 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
830 Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }),
831 );
832 }
833
834 #[test]
835 fn badly_signed_check_should_fail() {
836 let ux = Ex::new_signed(
837 vec![0u8; 0].into(),
838 TEST_ACCOUNT,
839 TestSig(TEST_ACCOUNT, vec![0u8; 0].into()),
840 DummyExtension,
841 );
842 assert!(!ux.is_inherent());
843 assert_eq!(
844 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
845 Err(InvalidTransaction::BadProof.into()),
846 );
847 }
848
849 #[test]
850 fn transaction_check_should_work() {
851 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
852 assert!(!ux.is_inherent());
853 assert_eq!(
854 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
855 Ok(CEx {
856 format: ExtrinsicFormat::General(0, DummyExtension),
857 function: vec![0u8; 0].into()
858 }),
859 );
860 }
861
862 #[test]
863 fn signed_check_should_work() {
864 let sig_payload = SignedPayload::from_raw(
865 FakeDispatchable::from(vec![0u8; 0]),
866 DummyExtension,
867 DummyExtension.implicit().unwrap(),
868 );
869 let ux = Ex::new_signed(
870 vec![0u8; 0].into(),
871 TEST_ACCOUNT,
872 TestSig(TEST_ACCOUNT, sig_payload.encode()),
873 DummyExtension,
874 );
875 assert!(!ux.is_inherent());
876 assert_eq!(
877 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
878 Ok(CEx {
879 format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension),
880 function: vec![0u8; 0].into()
881 }),
882 );
883 }
884
885 #[test]
886 fn encoding_matches_vec() {
887 let ex = Ex::new_bare(vec![0u8; 0].into());
888 let encoded = ex.encode();
889 let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
890 assert_eq!(decoded, ex);
891 let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
892 assert_eq!(as_vec.encode(), encoded);
893 }
894
895 #[test]
896 fn conversion_to_opaque() {
897 let ux = Ex::new_bare(vec![0u8; 0].into());
898 let encoded = ux.encode();
899 let opaque: OpaqueExtrinsic = ux.into();
900 let opaque_encoded = opaque.encode();
901 assert_eq!(opaque_encoded, encoded);
902 }
903
904 #[test]
905 fn large_bad_prefix_should_work() {
906 let encoded = (Compact::<u32>::from(u32::MAX), Preamble::<(), (), ()>::Bare(0)).encode();
907 assert!(Ex::decode(&mut &encoded[..]).is_err());
908 }
909
910 #[test]
911 fn legacy_short_signed_encode_decode() {
912 let call: TestCall = vec![0u8; 4].into();
913 let signed = TEST_ACCOUNT;
914 let extension = DummyExtension;
915 let implicit = extension.implicit().unwrap();
916 let legacy_signature = TestSig(TEST_ACCOUNT, (&call, &extension, &implicit).encode());
917
918 let old_ux =
919 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
920 call.clone(),
921 signed,
922 legacy_signature.clone(),
923 extension.clone(),
924 );
925
926 let encoded_old_ux = old_ux.encode();
927 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
928
929 assert_eq!(decoded_old_ux.function, call);
930 assert_eq!(
931 decoded_old_ux.preamble,
932 Preamble::Signed(signed, legacy_signature.clone(), extension.clone())
933 );
934
935 let new_ux =
936 Ex::new_signed(call.clone(), signed, legacy_signature.clone(), extension.clone());
937
938 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
939 let old_checked =
940 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
941 assert_eq!(new_checked, old_checked);
942 }
943
944 #[test]
945 fn legacy_long_signed_encode_decode() {
946 let call: TestCall = vec![0u8; 257].into();
947 let signed = TEST_ACCOUNT;
948 let extension = DummyExtension;
949 let implicit = extension.implicit().unwrap();
950 let signature = TestSig(
951 TEST_ACCOUNT,
952 blake2_256(&(&call, DummyExtension, &implicit).encode()[..]).to_vec(),
953 );
954
955 let old_ux =
956 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
957 call.clone(),
958 signed,
959 signature.clone(),
960 extension.clone(),
961 );
962
963 let encoded_old_ux = old_ux.encode();
964 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
965
966 assert_eq!(decoded_old_ux.function, call);
967 assert_eq!(
968 decoded_old_ux.preamble,
969 Preamble::Signed(signed, signature.clone(), extension.clone())
970 );
971
972 let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone());
973
974 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
975 let old_checked =
976 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
977 assert_eq!(new_checked, old_checked);
978 }
979
980 #[test]
981 fn legacy_unsigned_encode_decode() {
982 let call: TestCall = vec![0u8; 0].into();
983
984 let old_ux =
985 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_unsigned(
986 call.clone(),
987 );
988
989 let encoded_old_ux = old_ux.encode();
990 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
991
992 assert_eq!(decoded_old_ux.function, call);
993 assert_eq!(decoded_old_ux.preamble, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION));
994
995 let new_legacy_ux = Ex::new_bare_legacy(call.clone());
996 assert_eq!(encoded_old_ux, new_legacy_ux.encode());
997
998 let new_ux = Ex::new_bare(call.clone());
999 let encoded_new_ux = new_ux.encode();
1000 let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap();
1001 assert_eq!(new_ux, decoded_new_ux);
1002
1003 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1004 let old_checked =
1005 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1006 assert_eq!(new_checked, old_checked);
1007 }
1008}