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