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