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::{self, Debug};
38use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter};
39use sp_io::hashing::blake2_256;
40use sp_weights::Weight;
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#[cfg_attr(all(feature = "std", not(windows)), doc = simple_mermaid::mermaid!("../../docs/mermaid/extrinsics.mmd"))]
222#[derive(DecodeWithMemTracking, Eq, Clone)]
230#[codec(decode_with_mem_tracking_bound(
231 Address: DecodeWithMemTracking,
232 Call: DecodeWithMemTracking,
233 Signature: DecodeWithMemTracking,
234 Extension: DecodeWithMemTracking)
235)]
236pub struct UncheckedExtrinsic<
237 Address,
238 Call,
239 Signature,
240 Extension,
241 const MAX_CALL_SIZE: usize = DEFAULT_MAX_CALL_SIZE,
242> {
243 pub preamble: Preamble<Address, Signature, Extension>,
246 pub function: Call,
248 pub encoded_call: Option<Vec<u8>>,
256}
257
258impl<
259 Address: Debug,
260 Call: Debug,
261 Signature: Debug,
262 Extension: Debug,
263 const MAX_CALL_SIZE: usize,
264 > Debug for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
265{
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 f.debug_struct("UncheckedExtrinsic")
268 .field("preamble", &self.preamble)
269 .field("function", &self.function)
270 .finish()
271 }
272}
273
274impl<
275 Address: PartialEq,
276 Call: PartialEq,
277 Signature: PartialEq,
278 Extension: PartialEq,
279 const MAX_CALL_SIZE: usize,
280 > PartialEq for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
281{
282 fn eq(&self, other: &Self) -> bool {
283 self.preamble == other.preamble && self.function == other.function
284 }
285}
286
287impl<Address, Call, Signature, Extension> TypeInfo
292 for UncheckedExtrinsic<Address, Call, Signature, Extension>
293where
294 Address: StaticTypeInfo,
295 Call: StaticTypeInfo,
296 Signature: StaticTypeInfo,
297 Extension: StaticTypeInfo,
298{
299 type Identity = UncheckedExtrinsic<Address, Call, Signature, Extension>;
300
301 fn type_info() -> Type {
302 Type::builder()
303 .path(Path::new("UncheckedExtrinsic", module_path!()))
304 .type_params(vec![
308 TypeParameter::new("Address", Some(meta_type::<Address>())),
309 TypeParameter::new("Call", Some(meta_type::<Call>())),
310 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
311 TypeParameter::new("Extra", Some(meta_type::<Extension>())),
312 ])
313 .docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"])
314 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
318 }
319}
320
321impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize>
322 UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
323{
324 #[deprecated = "Use new_bare instead"]
328 pub fn new_unsigned(function: Call) -> Self {
329 Self::new_bare(function)
330 }
331
332 pub fn is_inherent(&self) -> bool {
334 matches!(self.preamble, Preamble::Bare(_))
335 }
336
337 pub fn is_signed(&self) -> bool {
340 matches!(self.preamble, Preamble::Signed(..))
341 }
342
343 pub fn from_parts(function: Call, preamble: Preamble<Address, Signature, Extension>) -> Self {
345 Self { preamble, function, encoded_call: None }
346 }
347
348 pub fn new_bare(function: Call) -> Self {
350 Self::from_parts(function, Preamble::Bare(EXTRINSIC_FORMAT_VERSION))
351 }
352
353 pub fn new_bare_legacy(function: Call) -> Self {
355 Self::from_parts(function, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION))
356 }
357
358 pub fn new_signed(
360 function: Call,
361 signed: Address,
362 signature: Signature,
363 tx_ext: Extension,
364 ) -> Self {
365 Self::from_parts(function, Preamble::Signed(signed, signature, tx_ext))
366 }
367
368 pub fn new_transaction(function: Call, tx_ext: Extension) -> Self {
370 Self::from_parts(function, Preamble::General(EXTENSION_VERSION, tx_ext))
371 }
372
373 fn decode_with_len<I: Input>(input: &mut I, len: usize) -> Result<Self, codec::Error>
374 where
375 Preamble<Address, Signature, Extension>: Decode,
376 Call: DecodeWithMemTracking,
377 {
378 let mut input = CountedInput::new(input);
379
380 let preamble = Decode::decode(&mut input)?;
381
382 struct CloneBytes<'a, I>(&'a mut I, Vec<u8>);
383 impl<I: Input> Input for CloneBytes<'_, I> {
384 fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
385 self.0.remaining_len()
386 }
387
388 fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
389 self.0.read(into)?;
390
391 self.1.extend_from_slice(into);
392 Ok(())
393 }
394
395 fn descend_ref(&mut self) -> Result<(), codec::Error> {
396 self.0.descend_ref()
397 }
398
399 fn ascend_ref(&mut self) {
400 self.0.ascend_ref();
401 }
402
403 fn on_before_alloc_mem(&mut self, size: usize) -> Result<(), codec::Error> {
404 self.0.on_before_alloc_mem(size)
405 }
406 }
407
408 let mut clone_bytes = CloneBytes(&mut input, Vec::new());
409
410 let function =
413 Call::decode_with_mem_limit(&mut clone_bytes, MAX_CALL_SIZE.saturating_add(1))?;
414
415 let encoded_call = Some(clone_bytes.1);
416
417 if input.count() != len as u64 {
418 return Err("Invalid length prefix".into())
419 }
420
421 Ok(Self { preamble, function, encoded_call })
422 }
423
424 fn encode_without_prefix(&self) -> Vec<u8>
425 where
426 Preamble<Address, Signature, Extension>: Encode,
427 Call: Encode,
428 {
429 let mut encoded = self.preamble.encode();
430
431 match &self.encoded_call {
432 Some(call) => {
433 encoded.extend(call);
434 },
435 None => {
436 self.function.encode_to(&mut encoded);
437 },
438 }
439
440 encoded
441 }
442}
443
444impl<Address, Call, Signature, Extension> ExtrinsicLike
445 for UncheckedExtrinsic<Address, Call, Signature, Extension>
446{
447 fn is_signed(&self) -> Option<bool> {
448 Some(matches!(self.preamble, Preamble::Signed(..)))
449 }
450
451 fn is_bare(&self) -> bool {
452 matches!(self.preamble, Preamble::Bare(_))
453 }
454}
455
456impl<Address, Call, Signature, Extra> ExtrinsicCall
457 for UncheckedExtrinsic<Address, Call, Signature, Extra>
458{
459 type Call = Call;
460
461 fn call(&self) -> &Call {
462 &self.function
463 }
464
465 fn into_call(self) -> Self::Call {
466 self.function
467 }
468}
469
470impl<LookupSource, AccountId, Call, Signature, Extension, Lookup> Checkable<Lookup>
475 for UncheckedExtrinsic<LookupSource, Call, Signature, Extension>
476where
477 LookupSource: Member + MaybeDisplay,
478 Call: Encode + Member + Dispatchable,
479 Signature: Member + traits::Verify,
480 <Signature as traits::Verify>::Signer: IdentifyAccount<AccountId = AccountId>,
481 Extension: Encode + TransactionExtension<Call>,
482 AccountId: Member + MaybeDisplay,
483 Lookup: traits::Lookup<Source = LookupSource, Target = AccountId>,
484{
485 type Checked = CheckedExtrinsic<AccountId, Call, Extension>;
486
487 fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
488 Ok(match self.preamble {
489 Preamble::Signed(signed, signature, tx_ext) => {
490 let signed = lookup.lookup(signed)?;
491 let raw_payload = SignedPayload::new(
493 CallAndMaybeEncoded { encoded: self.encoded_call, call: self.function },
494 tx_ext,
495 )?;
496 if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
497 return Err(InvalidTransaction::BadProof.into())
498 }
499 let (function, tx_ext, _) = raw_payload.deconstruct();
500 CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function }
501 },
502 Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
503 format: ExtrinsicFormat::General(extension_version, tx_ext),
504 function: self.function,
505 },
506 Preamble::Bare(_) =>
507 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function },
508 })
509 }
510
511 #[cfg(feature = "try-runtime")]
512 fn unchecked_into_checked_i_know_what_i_am_doing(
513 self,
514 lookup: &Lookup,
515 ) -> Result<Self::Checked, TransactionValidityError> {
516 Ok(match self.preamble {
517 Preamble::Signed(signed, _, tx_ext) => {
518 let signed = lookup.lookup(signed)?;
519 CheckedExtrinsic {
520 format: ExtrinsicFormat::Signed(signed, tx_ext),
521 function: self.function,
522 }
523 },
524 Preamble::General(extension_version, tx_ext) => CheckedExtrinsic {
525 format: ExtrinsicFormat::General(extension_version, tx_ext),
526 function: self.function,
527 },
528 Preamble::Bare(_) =>
529 CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function },
530 })
531 }
532}
533
534impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
535 ExtrinsicMetadata for UncheckedExtrinsic<Address, Call, Signature, Extension>
536{
537 const VERSIONS: &'static [u8] = &[LEGACY_EXTRINSIC_FORMAT_VERSION, EXTRINSIC_FORMAT_VERSION];
538 type TransactionExtensions = Extension;
539}
540
541impl<Address, Call: Dispatchable, Signature, Extension: TransactionExtension<Call>>
542 UncheckedExtrinsic<Address, Call, Signature, Extension>
543{
544 pub fn extension_weight(&self) -> Weight {
547 match &self.preamble {
548 Preamble::Bare(_) => Weight::zero(),
549 Preamble::Signed(_, _, ext) | Preamble::General(_, ext) => ext.weight(&self.function),
550 }
551 }
552}
553
554impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize> Decode
555 for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
556where
557 Address: Decode,
558 Signature: Decode,
559 Call: DecodeWithMemTracking,
560 Extension: Decode,
561{
562 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
563 let expected_length: Compact<u32> = Decode::decode(input)?;
567
568 Self::decode_with_len(input, expected_length.0 as usize)
569 }
570}
571
572#[docify::export(unchecked_extrinsic_encode_impl)]
573impl<Address, Call, Signature, Extension> Encode
574 for UncheckedExtrinsic<Address, Call, Signature, Extension>
575where
576 Preamble<Address, Signature, Extension>: Encode,
577 Call: Encode,
578 Extension: Encode,
579{
580 fn encode(&self) -> Vec<u8> {
581 let tmp = self.encode_without_prefix();
582
583 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
584
585 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
587
588 compact_len.encode_to(&mut output);
589 output.extend(tmp);
590
591 output
592 }
593}
594
595impl<Address, Call, Signature, Extension> EncodeLike
596 for UncheckedExtrinsic<Address, Call, Signature, Extension>
597where
598 Address: Encode,
599 Signature: Encode,
600 Call: Encode + Dispatchable,
601 Extension: TransactionExtension<Call>,
602{
603}
604
605#[cfg(feature = "serde")]
606impl<Address: Encode, Signature: Encode, Call: Encode, Extension: Encode> serde::Serialize
607 for UncheckedExtrinsic<Address, Call, Signature, Extension>
608{
609 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
610 where
611 S: ::serde::Serializer,
612 {
613 self.using_encoded(|bytes| seq.serialize_bytes(bytes))
614 }
615}
616
617#[cfg(feature = "serde")]
618impl<'a, Address: Decode, Signature: Decode, Call: DecodeWithMemTracking, Extension: Decode>
619 serde::Deserialize<'a> for UncheckedExtrinsic<Address, Call, Signature, Extension>
620{
621 fn deserialize<D>(de: D) -> Result<Self, D::Error>
622 where
623 D: serde::Deserializer<'a>,
624 {
625 let r = sp_core::bytes::deserialize(de)?;
626 Self::decode(&mut &r[..])
627 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
628 }
629}
630
631pub struct CallAndMaybeEncoded<T> {
633 encoded: Option<Vec<u8>>,
634 call: T,
635}
636
637impl<T> CallAndMaybeEncoded<T> {
638 pub fn into_call(self) -> T {
640 self.call
641 }
642}
643
644impl<T> From<T> for CallAndMaybeEncoded<T> {
645 fn from(value: T) -> Self {
646 Self { call: value, encoded: None }
647 }
648}
649
650impl<T: Encode> Encode for CallAndMaybeEncoded<T> {
651 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
652 match &self.encoded {
653 Some(enc) => f(&enc),
654 None => self.call.using_encoded(f),
655 }
656 }
657}
658
659pub struct SignedPayload<Call: Dispatchable, Extension: TransactionExtension<Call>>(
665 (CallAndMaybeEncoded<Call>, Extension, Extension::Implicit),
666);
667
668impl<Call, Extension> SignedPayload<Call, Extension>
669where
670 Call: Encode + Dispatchable,
671 Extension: TransactionExtension<Call>,
672{
673 pub fn new(
677 call: impl Into<CallAndMaybeEncoded<Call>>,
678 tx_ext: Extension,
679 ) -> Result<Self, TransactionValidityError> {
680 let implicit = Extension::implicit(&tx_ext)?;
681 Ok(Self((call.into(), tx_ext, implicit)))
682 }
683
684 pub fn from_raw(
686 call: impl Into<CallAndMaybeEncoded<Call>>,
687 tx_ext: Extension,
688 implicit: Extension::Implicit,
689 ) -> Self {
690 Self((call.into(), tx_ext, implicit))
691 }
692
693 pub fn deconstruct(self) -> (Call, Extension, Extension::Implicit) {
695 let (call, ext, implicit) = self.0;
696 (call.call, ext, implicit)
697 }
698}
699
700impl<Call, Extension> Encode for SignedPayload<Call, Extension>
701where
702 Call: Encode + Dispatchable,
703 Extension: TransactionExtension<Call>,
704{
705 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
707 self.0.using_encoded(|payload| {
708 if payload.len() > 256 {
709 f(&blake2_256(payload)[..])
710 } else {
711 f(payload)
712 }
713 })
714 }
715}
716
717impl<Call, Extension> EncodeLike for SignedPayload<Call, Extension>
718where
719 Call: Encode + Dispatchable,
720 Extension: TransactionExtension<Call>,
721{
722}
723
724impl<Address, Call, Signature, Extension>
725 From<UncheckedExtrinsic<Address, Call, Signature, Extension>> for OpaqueExtrinsic
726where
727 Preamble<Address, Signature, Extension>: Encode,
728 Call: Encode,
729{
730 fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extension>) -> Self {
731 Self::from_blob(extrinsic.encode_without_prefix())
732 }
733}
734
735impl<Address, Call, Signature, Extension, const MAX_CALL_SIZE: usize> LazyExtrinsic
736 for UncheckedExtrinsic<Address, Call, Signature, Extension, MAX_CALL_SIZE>
737where
738 Preamble<Address, Signature, Extension>: Decode,
739 Call: DecodeWithMemTracking,
740{
741 fn decode_unprefixed(data: &[u8]) -> Result<Self, codec::Error> {
742 Self::decode_with_len(&mut &data[..], data.len())
743 }
744}
745
746#[cfg(test)]
747mod legacy {
748 use codec::{Compact, Decode, Encode, EncodeLike, Error, Input};
749 use scale_info::{
750 build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter,
751 };
752
753 pub type UncheckedSignaturePayloadV4<Address, Signature, Extra> = (Address, Signature, Extra);
754
755 #[derive(PartialEq, Eq, Clone, Debug)]
756 pub struct UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
757 pub signature: Option<UncheckedSignaturePayloadV4<Address, Signature, Extra>>,
758 pub function: Call,
759 }
760
761 impl<Address, Call, Signature, Extra> TypeInfo
762 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
763 where
764 Address: StaticTypeInfo,
765 Call: StaticTypeInfo,
766 Signature: StaticTypeInfo,
767 Extra: StaticTypeInfo,
768 {
769 type Identity = UncheckedExtrinsicV4<Address, Call, Signature, Extra>;
770
771 fn type_info() -> Type {
772 Type::builder()
773 .path(Path::new("UncheckedExtrinsic", module_path!()))
774 .type_params(vec![
779 TypeParameter::new("Address", Some(meta_type::<Address>())),
780 TypeParameter::new("Call", Some(meta_type::<Call>())),
781 TypeParameter::new("Signature", Some(meta_type::<Signature>())),
782 TypeParameter::new("Extra", Some(meta_type::<Extra>())),
783 ])
784 .docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"])
785 .composite(Fields::unnamed().field(|f| f.ty::<Vec<u8>>()))
789 }
790 }
791
792 impl<Address, Call, Signature, Extra> UncheckedExtrinsicV4<Address, Call, Signature, Extra> {
793 pub fn new_signed(
794 function: Call,
795 signed: Address,
796 signature: Signature,
797 extra: Extra,
798 ) -> Self {
799 Self { signature: Some((signed, signature, extra)), function }
800 }
801
802 pub fn new_unsigned(function: Call) -> Self {
803 Self { signature: None, function }
804 }
805 }
806
807 impl<Address, Call, Signature, Extra> Decode
808 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
809 where
810 Address: Decode,
811 Signature: Decode,
812 Call: Decode,
813 Extra: Decode,
814 {
815 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
816 let expected_length: Compact<u32> = Decode::decode(input)?;
820 let before_length = input.remaining_len()?;
821
822 let version = input.read_byte()?;
823
824 let is_signed = version & 0b1000_0000 != 0;
825 let version = version & 0b0111_1111;
826 if version != 4u8 {
827 return Err("Invalid transaction version".into())
828 }
829
830 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
831 let function = Decode::decode(input)?;
832
833 if let Some((before_length, after_length)) =
834 input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
835 {
836 let length = before_length.saturating_sub(after_length);
837
838 if length != expected_length.0 as usize {
839 return Err("Invalid length prefix".into())
840 }
841 }
842
843 Ok(Self { signature, function })
844 }
845 }
846
847 #[docify::export(unchecked_extrinsic_encode_impl)]
848 impl<Address, Call, Signature, Extra> Encode
849 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
850 where
851 Address: Encode,
852 Signature: Encode,
853 Call: Encode,
854 Extra: Encode,
855 {
856 fn encode(&self) -> Vec<u8> {
857 let mut tmp = Vec::with_capacity(core::mem::size_of::<Self>());
858
859 match self.signature.as_ref() {
861 Some(s) => {
862 tmp.push(4u8 | 0b1000_0000);
863 s.encode_to(&mut tmp);
864 },
865 None => {
866 tmp.push(4u8 & 0b0111_1111);
867 },
868 }
869 self.function.encode_to(&mut tmp);
870
871 let compact_len = codec::Compact::<u32>(tmp.len() as u32);
872
873 let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len());
875
876 compact_len.encode_to(&mut output);
877 output.extend(tmp);
878
879 output
880 }
881 }
882
883 impl<Address, Call, Signature, Extra> EncodeLike
884 for UncheckedExtrinsicV4<Address, Call, Signature, Extra>
885 where
886 Address: Encode,
887 Signature: Encode,
888 Call: Encode,
889 Extra: Encode,
890 {
891 }
892}
893
894#[cfg(test)]
895mod tests {
896 use super::{legacy::UncheckedExtrinsicV4, *};
897 use crate::{
898 codec::{Decode, Encode},
899 impl_tx_ext_default,
900 testing::TestSignature as TestSig,
901 traits::{FakeDispatchable, IdentityLookup, TransactionExtension},
902 };
903 use sp_io::hashing::blake2_256;
904
905 type TestContext = IdentityLookup<u64>;
906 type TestAccountId = u64;
907
908 #[derive(Debug, Clone, Eq, PartialEq, Encode, TypeInfo)]
910 enum Call {
911 Raw(Vec<u8>),
912 Sort(Vec<u8>),
913 }
914
915 impl Decode for Call {
916 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
917 let variant = input.read_byte()?;
918 match variant {
919 0 => {
920 let data = Vec::<u8>::decode(input)?;
921 Ok(Call::Raw(data))
922 },
923 1 => {
924 let mut data = Vec::<u8>::decode(input)?;
925 data.sort();
927 Ok(Call::Sort(data))
928 },
929 _ => Err("Invalid Call variant".into()),
930 }
931 }
932 }
933
934 impl DecodeWithMemTracking for Call {}
935
936 impl From<Call> for Vec<u8> {
937 fn from(call: Call) -> Vec<u8> {
938 match call {
939 Call::Sort(data) | Call::Raw(data) => data,
940 }
941 }
942 }
943
944 impl From<Vec<u8>> for FakeDispatchable<Call> {
945 fn from(value: Vec<u8>) -> Self {
946 Self(Call::Raw(value))
947 }
948 }
949
950 type TestCall = FakeDispatchable<Call>;
951
952 const TEST_ACCOUNT: TestAccountId = 0;
953
954 #[derive(
956 Debug,
957 Encode,
958 Decode,
959 DecodeWithMemTracking,
960 Clone,
961 Eq,
962 PartialEq,
963 Ord,
964 PartialOrd,
965 TypeInfo,
966 )]
967 struct DummyExtension;
968 impl TransactionExtension<TestCall> for DummyExtension {
969 const IDENTIFIER: &'static str = "DummyExtension";
970 type Implicit = ();
971 type Val = ();
972 type Pre = ();
973 impl_tx_ext_default!(TestCall; weight validate prepare);
974 }
975
976 type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, DummyExtension>;
977 type CEx = CheckedExtrinsic<TestAccountId, TestCall, DummyExtension>;
978
979 #[test]
980 fn unsigned_codec_should_work() {
981 let call: TestCall = Call::Raw(vec![0u8; 0]).into();
982 let ux = Ex::new_bare(call);
983 let encoded = ux.encode();
984 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
985 }
986
987 #[test]
988 fn invalid_length_prefix_is_detected() {
989 let ux = Ex::new_bare(Call::Raw(vec![0u8; 0]).into());
990 let mut encoded = ux.encode();
991
992 let length = Compact::<u32>::decode(&mut &encoded[..]).unwrap();
993 Compact(length.0 + 10).encode_to(&mut &mut encoded[..1]);
994
995 assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into()));
996 }
997
998 #[test]
999 fn transaction_codec_should_work() {
1000 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
1001 let encoded = ux.encode();
1002 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1003 }
1004
1005 #[test]
1006 fn signed_codec_should_work() {
1007 let ux = Ex::new_signed(
1008 vec![0u8; 0].into(),
1009 TEST_ACCOUNT,
1010 TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()),
1011 DummyExtension,
1012 );
1013 let encoded = ux.encode();
1014 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1015 }
1016
1017 #[test]
1018 fn large_signed_codec_should_work() {
1019 let ux = Ex::new_signed(
1020 vec![0u8; 0].into(),
1021 TEST_ACCOUNT,
1022 TestSig(
1023 TEST_ACCOUNT,
1024 (vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(),
1025 ),
1026 DummyExtension,
1027 );
1028 let encoded = ux.encode();
1029 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1030 }
1031
1032 #[test]
1033 fn unsigned_check_should_work() {
1034 let ux = Ex::new_bare(vec![0u8; 0].into());
1035 assert!(ux.is_inherent());
1036 assert_eq!(
1037 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1038 Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }),
1039 );
1040 }
1041
1042 #[test]
1043 fn badly_signed_check_should_fail() {
1044 let ux = Ex::new_signed(
1045 vec![0u8; 0].into(),
1046 TEST_ACCOUNT,
1047 TestSig(TEST_ACCOUNT, vec![0u8; 0].into()),
1048 DummyExtension,
1049 );
1050 assert!(!ux.is_inherent());
1051 assert_eq!(
1052 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1053 Err(InvalidTransaction::BadProof.into()),
1054 );
1055 }
1056
1057 #[test]
1058 fn transaction_check_should_work() {
1059 let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension);
1060 assert!(!ux.is_inherent());
1061 assert_eq!(
1062 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1063 Ok(CEx {
1064 format: ExtrinsicFormat::General(0, DummyExtension),
1065 function: vec![0u8; 0].into()
1066 }),
1067 );
1068 }
1069
1070 #[test]
1071 fn signed_check_should_work() {
1072 let sig_payload = SignedPayload::from_raw(
1073 FakeDispatchable::from(vec![0u8; 0]),
1074 DummyExtension,
1075 DummyExtension.implicit().unwrap(),
1076 );
1077 let ux = Ex::new_signed(
1078 vec![0u8; 0].into(),
1079 TEST_ACCOUNT,
1080 TestSig(TEST_ACCOUNT, sig_payload.encode()),
1081 DummyExtension,
1082 );
1083 assert!(!ux.is_inherent());
1084 assert_eq!(
1085 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
1086 Ok(CEx {
1087 format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension),
1088 function: Call::Raw(vec![0u8; 0]).into()
1089 }),
1090 );
1091 }
1092
1093 #[test]
1094 fn encoding_matches_vec() {
1095 let ex = Ex::new_bare(Call::Raw(vec![0u8; 0]).into());
1096 let encoded = ex.encode();
1097 let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
1098 assert_eq!(decoded, ex);
1099 let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
1100 assert_eq!(as_vec.encode(), encoded);
1101 }
1102
1103 #[test]
1104 fn conversion_to_opaque() {
1105 let ux = Ex::new_bare(Call::Raw(vec![0u8; 0]).into());
1106 let encoded = ux.encode();
1107 let opaque: OpaqueExtrinsic = ux.into();
1108 let opaque_encoded = opaque.encode();
1109 assert_eq!(opaque_encoded, encoded);
1110 }
1111
1112 #[test]
1113 fn large_bad_prefix_should_work() {
1114 let encoded = (Compact::<u32>::from(u32::MAX), Preamble::<(), (), ()>::Bare(0)).encode();
1115 assert!(Ex::decode(&mut &encoded[..]).is_err());
1116 }
1117
1118 #[test]
1119 fn legacy_short_signed_encode_decode() {
1120 let call: TestCall = Call::Raw(vec![0u8; 4]).into();
1121 let signed = TEST_ACCOUNT;
1122 let extension = DummyExtension;
1123 let implicit = extension.implicit().unwrap();
1124 let legacy_signature = TestSig(TEST_ACCOUNT, (&call, &extension, &implicit).encode());
1125
1126 let old_ux =
1127 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
1128 call.clone(),
1129 signed,
1130 legacy_signature.clone(),
1131 extension.clone(),
1132 );
1133
1134 let encoded_old_ux = old_ux.encode();
1135 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1136
1137 assert_eq!(decoded_old_ux.function, call);
1138 assert_eq!(
1139 decoded_old_ux.preamble,
1140 Preamble::Signed(signed, legacy_signature.clone(), extension.clone())
1141 );
1142
1143 let new_ux =
1144 Ex::new_signed(call.clone(), signed, legacy_signature.clone(), extension.clone());
1145
1146 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1147 let old_checked =
1148 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1149 assert_eq!(new_checked, old_checked);
1150 }
1151
1152 #[test]
1153 fn legacy_long_signed_encode_decode() {
1154 let call: TestCall = Call::Raw(vec![0u8; 257]).into();
1155 let signed = TEST_ACCOUNT;
1156 let extension = DummyExtension;
1157 let implicit = extension.implicit().unwrap();
1158 let signature = TestSig(
1159 TEST_ACCOUNT,
1160 blake2_256(&(&call, DummyExtension, &implicit).encode()[..]).to_vec(),
1161 );
1162
1163 let old_ux =
1164 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_signed(
1165 call.clone(),
1166 signed,
1167 signature.clone(),
1168 extension.clone(),
1169 );
1170
1171 let encoded_old_ux = old_ux.encode();
1172 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1173
1174 assert_eq!(decoded_old_ux.function, call);
1175 assert_eq!(
1176 decoded_old_ux.preamble,
1177 Preamble::Signed(signed, signature.clone(), extension.clone())
1178 );
1179
1180 let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone());
1181
1182 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1183 let old_checked =
1184 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1185 assert_eq!(new_checked, old_checked);
1186 }
1187
1188 #[test]
1189 fn legacy_unsigned_encode_decode() {
1190 let call: TestCall = Call::Raw(vec![0u8; 0]).into();
1191
1192 let old_ux =
1193 UncheckedExtrinsicV4::<TestAccountId, TestCall, TestSig, DummyExtension>::new_unsigned(
1194 call.clone(),
1195 );
1196
1197 let encoded_old_ux = old_ux.encode();
1198 let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap();
1199
1200 assert_eq!(decoded_old_ux.function, call);
1201 assert_eq!(decoded_old_ux.preamble, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION));
1202
1203 let new_legacy_ux = Ex::new_bare_legacy(call.clone());
1204 assert_eq!(encoded_old_ux, new_legacy_ux.encode());
1205
1206 let new_ux = Ex::new_bare(call.clone());
1207 let encoded_new_ux = new_ux.encode();
1208 let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap();
1209 assert_eq!(new_ux, decoded_new_ux);
1210
1211 let new_checked = new_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1212 let old_checked =
1213 decoded_old_ux.check(&IdentityLookup::<TestAccountId>::default()).unwrap();
1214 assert_eq!(new_checked, old_checked);
1215 }
1216
1217 #[test]
1218 fn max_call_heap_size_should_be_checked() {
1219 let ux = Ex::new_bare(Call::Raw(vec![0u8; DEFAULT_MAX_CALL_SIZE]).into());
1222 let encoded = ux.encode();
1223 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
1224
1225 let ux = Ex::new_bare(Call::Raw(vec![0u8; DEFAULT_MAX_CALL_SIZE + 1]).into());
1227 let encoded = ux.encode();
1228 assert_eq!(
1229 Ex::decode(&mut &encoded[..]).unwrap_err().to_string(),
1230 "Could not decode `FakeDispatchable.0`:\n\tHeap memory limit exceeded while decoding\n"
1231 );
1232 }
1233
1234 #[test]
1237 fn encoding_is_stable() {
1238 let unsorted_data = vec![5u8, 3, 7, 1, 9, 2, 8, 4, 6, 0];
1240 let call = Call::Sort(unsorted_data.clone());
1241
1242 let unsorted_encoded = call.encode();
1243
1244 let sig_payload = SignedPayload::from_raw(
1245 FakeDispatchable::from(call.clone()),
1246 DummyExtension,
1247 DummyExtension.implicit().unwrap(),
1248 );
1249 let sig_payload_encoded = sig_payload.encode();
1250
1251 let ux = Ex::new_signed(
1252 call.into(),
1253 TEST_ACCOUNT,
1254 TestSig(TEST_ACCOUNT, sig_payload_encoded.clone()),
1255 DummyExtension,
1256 );
1257
1258 let encoded = ux.encode();
1261 let decoded_ux = Ex::decode(&mut &encoded[..]).unwrap();
1262
1263 let mut expected_sorted_data = unsorted_data;
1265 expected_sorted_data.sort();
1266
1267 let expected_decoded_call =
1268 FakeDispatchable::from(Call::Sort(expected_sorted_data.clone()));
1269 assert_eq!(decoded_ux.function, expected_decoded_call);
1270
1271 let sorted_encoded = Call::Sort(expected_sorted_data).encode();
1273 assert_ne!(
1274 unsorted_encoded, sorted_encoded,
1275 "Sorted and unsorted should encode differently"
1276 );
1277
1278 assert_eq!(
1280 <Ex as Checkable<TestContext>>::check(decoded_ux, &Default::default())
1281 .unwrap()
1282 .function,
1283 expected_decoded_call,
1284 )
1285 }
1286}