1use super::BlockError;
6use crate::blocks::extrinsic_transaction_extensions::ExtrinsicTransactionExtensions;
7use crate::{
8 Metadata,
9 config::{Config, HashFor, Hasher},
10 error::{Error, MetadataError},
11};
12use alloc::sync::Arc;
13use alloc::vec::Vec;
14use core::ops::Deref;
15use frame_decode::extrinsics::Extrinsic;
16use scale_decode::DecodeAsType;
17use subxt_metadata::PalletMetadata;
18
19pub use crate::blocks::StaticExtrinsic;
20
21pub struct Extrinsics<T: Config> {
23 extrinsics: Vec<Arc<(Extrinsic<'static, u32>, Vec<u8>)>>,
24 metadata: Metadata,
25 hasher: T::Hasher,
26 _marker: core::marker::PhantomData<T>,
27}
28
29impl<T: Config> Extrinsics<T> {
30 pub fn decode_from(extrinsics: Vec<Vec<u8>>, metadata: Metadata) -> Result<Self, Error> {
34 let hasher = T::Hasher::new(&metadata);
35 let extrinsics = extrinsics
36 .into_iter()
37 .enumerate()
38 .map(|(extrinsic_index, bytes)| {
39 let cursor = &mut &*bytes;
40
41 let decoded_info = frame_decode::extrinsics::decode_extrinsic(
43 cursor,
44 metadata.deref(),
45 metadata.types(),
46 )
47 .map_err(|error| BlockError::ExtrinsicDecodeError {
48 extrinsic_index,
49 error,
50 })?
51 .into_owned();
52
53 if !cursor.is_empty() {
55 return Err(BlockError::LeftoverBytes {
56 extrinsic_index,
57 num_leftover_bytes: cursor.len(),
58 }
59 .into());
60 }
61
62 Ok(Arc::new((decoded_info, bytes)))
63 })
64 .collect::<Result<_, Error>>()?;
65
66 Ok(Self {
67 extrinsics,
68 hasher,
69 metadata,
70 _marker: core::marker::PhantomData,
71 })
72 }
73
74 pub fn len(&self) -> usize {
76 self.extrinsics.len()
77 }
78
79 pub fn is_empty(&self) -> bool {
82 self.extrinsics.is_empty()
83 }
84
85 pub fn iter(&self) -> impl Iterator<Item = ExtrinsicDetails<T>> + Send + Sync + 'static {
89 let extrinsics = self.extrinsics.clone();
90 let num_extrinsics = self.extrinsics.len();
91 let hasher = self.hasher;
92 let metadata = self.metadata.clone();
93
94 (0..num_extrinsics).map(move |index| {
95 ExtrinsicDetails::new(
96 index as u32,
97 extrinsics[index].clone(),
98 hasher,
99 metadata.clone(),
100 )
101 })
102 }
103
104 pub fn find<E: StaticExtrinsic>(
108 &self,
109 ) -> impl Iterator<Item = Result<FoundExtrinsic<T, E>, Error>> {
110 self.iter().filter_map(|details| {
111 match details.as_extrinsic::<E>() {
112 Err(err) => Some(Err(err)),
114 Ok(None) => None,
116 Ok(Some(value)) => Some(Ok(FoundExtrinsic { details, value })),
117 }
118 })
119 }
120
121 pub fn find_first<E: StaticExtrinsic>(&self) -> Result<Option<FoundExtrinsic<T, E>>, Error> {
124 self.find::<E>().next().transpose()
125 }
126
127 pub fn find_last<E: StaticExtrinsic>(&self) -> Result<Option<FoundExtrinsic<T, E>>, Error> {
130 self.find::<E>().last().transpose()
131 }
132
133 pub fn has<E: StaticExtrinsic>(&self) -> Result<bool, Error> {
135 Ok(self.find::<E>().next().transpose()?.is_some())
136 }
137}
138
139pub struct ExtrinsicDetails<T: Config> {
141 index: u32,
143 ext: Arc<(Extrinsic<'static, u32>, Vec<u8>)>,
145 hasher: T::Hasher,
147 metadata: Metadata,
149 _marker: core::marker::PhantomData<T>,
150}
151
152impl<T> ExtrinsicDetails<T>
153where
154 T: Config,
155{
156 #[doc(hidden)]
158 pub fn new(
159 index: u32,
160 ext: Arc<(Extrinsic<'static, u32>, Vec<u8>)>,
161 hasher: T::Hasher,
162 metadata: Metadata,
163 ) -> ExtrinsicDetails<T> {
164 ExtrinsicDetails {
165 index,
166 ext,
167 hasher,
168 metadata,
169 _marker: core::marker::PhantomData,
170 }
171 }
172
173 pub fn hash(&self) -> HashFor<T> {
175 self.hasher.hash(self.bytes())
177 }
178
179 pub fn is_signed(&self) -> bool {
181 self.decoded_info().is_signed()
182 }
183
184 pub fn index(&self) -> u32 {
186 self.index
187 }
188
189 pub fn bytes(&self) -> &[u8] {
197 &self.ext.1
198 }
199
200 pub fn call_bytes(&self) -> &[u8] {
209 &self.bytes()[self.decoded_info().call_data_range()]
210 }
211
212 pub fn field_bytes(&self) -> &[u8] {
219 &self.bytes()[self.decoded_info().call_data_args_range()]
222 }
223
224 pub fn address_bytes(&self) -> Option<&[u8]> {
230 self.decoded_info()
231 .signature_payload()
232 .map(|s| &self.bytes()[s.address_range()])
233 }
234
235 pub fn signature_bytes(&self) -> Option<&[u8]> {
237 self.decoded_info()
238 .signature_payload()
239 .map(|s| &self.bytes()[s.signature_range()])
240 }
241
242 pub fn transaction_extensions_bytes(&self) -> Option<&[u8]> {
249 self.decoded_info()
250 .transaction_extension_payload()
251 .map(|t| &self.bytes()[t.range()])
252 }
253
254 pub fn transaction_extensions(&self) -> Option<ExtrinsicTransactionExtensions<'_, T>> {
256 self.decoded_info()
257 .transaction_extension_payload()
258 .map(|t| ExtrinsicTransactionExtensions::new(self.bytes(), &self.metadata, t))
259 }
260
261 pub fn pallet_index(&self) -> u8 {
263 self.decoded_info().pallet_index()
264 }
265
266 pub fn variant_index(&self) -> u8 {
268 self.decoded_info().call_index()
269 }
270
271 pub fn pallet_name(&self) -> Result<&str, Error> {
273 Ok(self.extrinsic_metadata()?.pallet.name())
274 }
275
276 pub fn variant_name(&self) -> Result<&str, Error> {
278 Ok(&self.extrinsic_metadata()?.variant.name)
279 }
280
281 pub fn extrinsic_metadata(&self) -> Result<ExtrinsicMetadataDetails<'_>, Error> {
283 let pallet = self.metadata.pallet_by_index_err(self.pallet_index())?;
284 let variant = pallet
285 .call_variant_by_index(self.variant_index())
286 .ok_or_else(|| MetadataError::VariantIndexNotFound(self.variant_index()))?;
287
288 Ok(ExtrinsicMetadataDetails { pallet, variant })
289 }
290
291 pub fn field_values(&self) -> Result<scale_value::Composite<u32>, Error> {
294 let bytes = &mut self.field_bytes();
295 let extrinsic_metadata = self.extrinsic_metadata()?;
296
297 let mut fields = extrinsic_metadata
298 .variant
299 .fields
300 .iter()
301 .map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
302 let decoded =
303 scale_value::scale::decode_as_fields(bytes, &mut fields, self.metadata.types())?;
304
305 Ok(decoded)
306 }
307
308 pub fn as_extrinsic<E: StaticExtrinsic>(&self) -> Result<Option<E>, Error> {
311 let extrinsic_metadata = self.extrinsic_metadata()?;
312 if extrinsic_metadata.pallet.name() == E::PALLET
313 && extrinsic_metadata.variant.name == E::CALL
314 {
315 let mut fields = extrinsic_metadata
316 .variant
317 .fields
318 .iter()
319 .map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
320 let decoded =
321 E::decode_as_fields(&mut self.field_bytes(), &mut fields, self.metadata.types())?;
322 Ok(Some(decoded))
323 } else {
324 Ok(None)
325 }
326 }
327
328 pub fn as_root_extrinsic<E: DecodeAsType>(&self) -> Result<E, Error> {
332 let decoded = E::decode_as_type(
333 &mut &self.call_bytes()[..],
334 self.metadata.outer_enums().call_enum_ty(),
335 self.metadata.types(),
336 )?;
337
338 Ok(decoded)
339 }
340
341 fn decoded_info(&self) -> &Extrinsic<'static, u32> {
342 &self.ext.0
343 }
344}
345
346pub struct FoundExtrinsic<T: Config, E> {
348 pub details: ExtrinsicDetails<T>,
350 pub value: E,
352}
353
354pub struct ExtrinsicMetadataDetails<'a> {
356 pub pallet: PalletMetadata<'a>,
358 pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365 use crate::config::SubstrateConfig;
366 use assert_matches::assert_matches;
367 use codec::{Decode, Encode};
368 use frame_metadata::v15::{CustomMetadata, OuterEnums};
369 use frame_metadata::{
370 RuntimeMetadataPrefixed,
371 v15::{ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, RuntimeMetadataV15},
372 };
373 use scale_info::{TypeInfo, meta_type};
374 use scale_value::Value;
375
376 #[allow(unused)]
380 #[derive(TypeInfo)]
381 struct ExtrinsicType<Address, Call, Signature, Extra> {
382 pub signature: Option<(Address, Signature, Extra)>,
383 pub function: Call,
384 }
385
386 #[allow(unused)]
389 #[derive(
390 Encode,
391 Decode,
392 TypeInfo,
393 Clone,
394 Debug,
395 PartialEq,
396 Eq,
397 scale_encode::EncodeAsType,
398 scale_decode::DecodeAsType,
399 )]
400 enum RuntimeCall {
401 Test(Pallet),
402 }
403
404 #[allow(unused)]
406 #[derive(
407 Encode,
408 Decode,
409 TypeInfo,
410 Clone,
411 Debug,
412 PartialEq,
413 Eq,
414 scale_encode::EncodeAsType,
415 scale_decode::DecodeAsType,
416 )]
417 enum Pallet {
418 #[allow(unused)]
419 #[codec(index = 2)]
420 TestCall {
421 value: u128,
422 signed: bool,
423 name: String,
424 },
425 }
426
427 #[allow(unused)]
428 #[derive(
429 Encode,
430 Decode,
431 TypeInfo,
432 Clone,
433 Debug,
434 PartialEq,
435 Eq,
436 scale_encode::EncodeAsType,
437 scale_decode::DecodeAsType,
438 )]
439 struct TestCallExtrinsic {
440 value: u128,
441 signed: bool,
442 name: String,
443 }
444
445 impl StaticExtrinsic for TestCallExtrinsic {
446 const PALLET: &'static str = "Test";
447 const CALL: &'static str = "TestCall";
448 }
449
450 fn metadata() -> Metadata {
452 let pallets = vec![PalletMetadata {
453 name: "Test",
454 storage: None,
455 calls: Some(PalletCallMetadata {
456 ty: meta_type::<Pallet>(),
457 }),
458 event: None,
459 constants: vec![],
460 error: None,
461 index: 0,
462 docs: vec![],
463 }];
464
465 let extrinsic = ExtrinsicMetadata {
466 version: 4,
467 signed_extensions: vec![],
468 address_ty: meta_type::<()>(),
469 call_ty: meta_type::<RuntimeCall>(),
470 signature_ty: meta_type::<()>(),
471 extra_ty: meta_type::<()>(),
472 };
473
474 let meta = RuntimeMetadataV15::new(
475 pallets,
476 extrinsic,
477 meta_type::<()>(),
478 vec![],
479 OuterEnums {
480 call_enum_ty: meta_type::<RuntimeCall>(),
481 event_enum_ty: meta_type::<()>(),
482 error_enum_ty: meta_type::<()>(),
483 },
484 CustomMetadata {
485 map: Default::default(),
486 },
487 );
488 let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
489 let metadata: subxt_metadata::Metadata = runtime_metadata.try_into().unwrap();
490
491 Metadata::from(metadata)
492 }
493
494 #[test]
495 fn extrinsic_metadata_consistency() {
496 let metadata = metadata();
497
498 let pallet = metadata.pallet_by_index(0).expect("pallet exists");
500 let extrinsic = pallet
501 .call_variant_by_index(2)
502 .expect("metadata contains the RuntimeCall enum with this pallet");
503
504 assert_eq!(pallet.name(), "Test");
505 assert_eq!(&extrinsic.name, "TestCall");
506 }
507
508 #[test]
509 fn insufficient_extrinsic_bytes() {
510 let metadata = metadata();
511
512 let result = Extrinsics::<SubstrateConfig>::decode_from(vec![vec![]], metadata);
514 assert_matches!(
515 result.err(),
516 Some(crate::Error::Block(
517 crate::error::BlockError::ExtrinsicDecodeError {
518 extrinsic_index: 0,
519 error: _
520 }
521 ))
522 );
523 }
524
525 #[test]
526 fn unsupported_version_extrinsic() {
527 use frame_decode::extrinsics::ExtrinsicDecodeError;
528
529 let metadata = metadata();
530
531 let result = Extrinsics::<SubstrateConfig>::decode_from(vec![vec![3u8].encode()], metadata);
533
534 assert_matches!(
535 result.err(),
536 Some(crate::Error::Block(
537 crate::error::BlockError::ExtrinsicDecodeError {
538 extrinsic_index: 0,
539 error: ExtrinsicDecodeError::VersionNotSupported(3),
540 }
541 ))
542 );
543 }
544
545 #[test]
546 fn tx_hashes_line_up() {
547 let metadata = metadata();
548 let hasher = <SubstrateConfig as Config>::Hasher::new(&metadata);
549
550 let tx = crate::dynamic::tx(
551 "Test",
552 "TestCall",
553 vec![
554 Value::u128(10),
555 Value::bool(true),
556 Value::string("SomeValue"),
557 ],
558 );
559
560 let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
562 .expect("Valid dynamic parameters are provided");
563
564 let extrinsics = Extrinsics::<SubstrateConfig>::decode_from(
566 vec![tx_encoded.encoded().to_owned()],
567 metadata,
568 )
569 .expect("Valid extrinsic");
570
571 let extrinsic = extrinsics.iter().next().unwrap();
572
573 assert_eq!(tx_encoded.encoded(), extrinsic.bytes(), "bytes should eq");
575 assert_eq!(
577 tx_encoded.hash_with(hasher),
578 extrinsic.hash(),
579 "hashes should eq"
580 );
581 }
582
583 #[test]
584 fn statically_decode_extrinsic() {
585 let metadata = metadata();
586
587 let tx = crate::dynamic::tx(
588 "Test",
589 "TestCall",
590 vec![
591 Value::u128(10),
592 Value::bool(true),
593 Value::string("SomeValue"),
594 ],
595 );
596 let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
597 .expect("Valid dynamic parameters are provided");
598
599 let extrinsics = Extrinsics::<SubstrateConfig>::decode_from(
602 vec![tx_encoded.encoded().to_owned()],
603 metadata,
604 )
605 .expect("Valid extrinsic");
606
607 let extrinsic = extrinsics.iter().next().unwrap();
608
609 assert!(!extrinsic.is_signed());
610
611 assert_eq!(extrinsic.index(), 0);
612
613 assert_eq!(extrinsic.pallet_index(), 0);
614 assert_eq!(
615 extrinsic
616 .pallet_name()
617 .expect("Valid metadata contains pallet name"),
618 "Test"
619 );
620
621 assert_eq!(extrinsic.variant_index(), 2);
622 assert_eq!(
623 extrinsic
624 .variant_name()
625 .expect("Valid metadata contains variant name"),
626 "TestCall"
627 );
628
629 let decoded_extrinsic = extrinsic
631 .as_root_extrinsic::<RuntimeCall>()
632 .expect("can decode extrinsic to root enum");
633
634 assert_eq!(
635 decoded_extrinsic,
636 RuntimeCall::Test(Pallet::TestCall {
637 value: 10,
638 signed: true,
639 name: "SomeValue".into(),
640 })
641 );
642
643 let decoded_extrinsic = extrinsic
645 .as_extrinsic::<TestCallExtrinsic>()
646 .expect("can decode extrinsic to extrinsic variant")
647 .expect("value cannot be None");
648
649 assert_eq!(
650 decoded_extrinsic,
651 TestCallExtrinsic {
652 value: 10,
653 signed: true,
654 name: "SomeValue".into(),
655 }
656 );
657 }
658}