1use super::BlockError;
6use crate::blocks::extrinsic_transaction_extensions::ExtrinsicTransactionExtensions;
7use crate::{
8 config::{Config, Hasher},
9 error::{Error, MetadataError},
10 Metadata,
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 _marker: core::marker::PhantomData<T>,
26}
27
28impl<T: Config> Extrinsics<T> {
29 pub fn decode_from(extrinsics: Vec<Vec<u8>>, metadata: Metadata) -> Result<Self, Error> {
33 let extrinsics = extrinsics
34 .into_iter()
35 .enumerate()
36 .map(|(extrinsic_index, bytes)| {
37 let cursor = &mut &*bytes;
38
39 let decoded_info = frame_decode::extrinsics::decode_extrinsic(
41 cursor,
42 metadata.deref(),
43 metadata.types(),
44 )
45 .map_err(|error| BlockError::ExtrinsicDecodeError {
46 extrinsic_index,
47 error,
48 })?
49 .into_owned();
50
51 if !cursor.is_empty() {
53 return Err(BlockError::LeftoverBytes {
54 extrinsic_index,
55 num_leftover_bytes: cursor.len(),
56 }
57 .into());
58 }
59
60 Ok(Arc::new((decoded_info, bytes)))
61 })
62 .collect::<Result<_, Error>>()?;
63
64 Ok(Self {
65 extrinsics,
66 metadata,
67 _marker: core::marker::PhantomData,
68 })
69 }
70
71 pub fn len(&self) -> usize {
73 self.extrinsics.len()
74 }
75
76 pub fn is_empty(&self) -> bool {
79 self.extrinsics.is_empty()
80 }
81
82 pub fn iter(&self) -> impl Iterator<Item = ExtrinsicDetails<T>> + Send + Sync + 'static {
86 let extrinsics = self.extrinsics.clone();
87 let num_extrinsics = self.extrinsics.len();
88 let metadata = self.metadata.clone();
89
90 (0..num_extrinsics).map(move |index| {
91 ExtrinsicDetails::new(index as u32, extrinsics[index].clone(), metadata.clone())
92 })
93 }
94
95 pub fn find<E: StaticExtrinsic>(
99 &self,
100 ) -> impl Iterator<Item = Result<FoundExtrinsic<T, E>, Error>> + '_ {
101 self.iter().filter_map(|details| {
102 match details.as_extrinsic::<E>() {
103 Err(err) => Some(Err(err)),
105 Ok(None) => None,
107 Ok(Some(value)) => Some(Ok(FoundExtrinsic { details, value })),
108 }
109 })
110 }
111
112 pub fn find_first<E: StaticExtrinsic>(&self) -> Result<Option<FoundExtrinsic<T, E>>, Error> {
115 self.find::<E>().next().transpose()
116 }
117
118 pub fn find_last<E: StaticExtrinsic>(&self) -> Result<Option<FoundExtrinsic<T, E>>, Error> {
121 self.find::<E>().last().transpose()
122 }
123
124 pub fn has<E: StaticExtrinsic>(&self) -> Result<bool, Error> {
126 Ok(self.find::<E>().next().transpose()?.is_some())
127 }
128}
129
130pub struct ExtrinsicDetails<T: Config> {
132 index: u32,
134 ext: Arc<(Extrinsic<'static, u32>, Vec<u8>)>,
136 metadata: Metadata,
138 _marker: core::marker::PhantomData<T>,
139}
140
141impl<T> ExtrinsicDetails<T>
142where
143 T: Config,
144{
145 #[doc(hidden)]
147 pub fn new(
148 index: u32,
149 ext: Arc<(Extrinsic<'static, u32>, Vec<u8>)>,
150 metadata: Metadata,
151 ) -> ExtrinsicDetails<T> {
152 ExtrinsicDetails {
153 index,
154 ext,
155 metadata,
156 _marker: core::marker::PhantomData,
157 }
158 }
159
160 pub fn hash(&self) -> T::Hash {
162 T::Hasher::hash(self.bytes())
164 }
165
166 pub fn is_signed(&self) -> bool {
168 self.decoded_info().is_signed()
169 }
170
171 pub fn index(&self) -> u32 {
173 self.index
174 }
175
176 pub fn bytes(&self) -> &[u8] {
184 &self.ext.1
185 }
186
187 pub fn call_bytes(&self) -> &[u8] {
196 &self.bytes()[self.decoded_info().call_data_range()]
197 }
198
199 pub fn field_bytes(&self) -> &[u8] {
206 &self.bytes()[self.decoded_info().call_data_args_range()]
209 }
210
211 pub fn address_bytes(&self) -> Option<&[u8]> {
217 self.decoded_info()
218 .signature_payload()
219 .map(|s| &self.bytes()[s.address_range()])
220 }
221
222 pub fn signature_bytes(&self) -> Option<&[u8]> {
224 self.decoded_info()
225 .signature_payload()
226 .map(|s| &self.bytes()[s.signature_range()])
227 }
228
229 pub fn transaction_extensions_bytes(&self) -> Option<&[u8]> {
236 self.decoded_info()
237 .transaction_extension_payload()
238 .map(|t| &self.bytes()[t.range()])
239 }
240
241 pub fn transaction_extensions(&self) -> Option<ExtrinsicTransactionExtensions<'_, T>> {
243 self.decoded_info()
244 .transaction_extension_payload()
245 .map(|t| ExtrinsicTransactionExtensions::new(self.bytes(), &self.metadata, t))
246 }
247
248 pub fn pallet_index(&self) -> u8 {
250 self.decoded_info().pallet_index()
251 }
252
253 pub fn variant_index(&self) -> u8 {
255 self.decoded_info().call_index()
256 }
257
258 pub fn pallet_name(&self) -> Result<&str, Error> {
260 Ok(self.extrinsic_metadata()?.pallet.name())
261 }
262
263 pub fn variant_name(&self) -> Result<&str, Error> {
265 Ok(&self.extrinsic_metadata()?.variant.name)
266 }
267
268 pub fn extrinsic_metadata(&self) -> Result<ExtrinsicMetadataDetails, Error> {
270 let pallet = self.metadata.pallet_by_index_err(self.pallet_index())?;
271 let variant = pallet
272 .call_variant_by_index(self.variant_index())
273 .ok_or_else(|| MetadataError::VariantIndexNotFound(self.variant_index()))?;
274
275 Ok(ExtrinsicMetadataDetails { pallet, variant })
276 }
277
278 pub fn field_values(&self) -> Result<scale_value::Composite<u32>, Error> {
281 let bytes = &mut self.field_bytes();
282 let extrinsic_metadata = self.extrinsic_metadata()?;
283
284 let mut fields = extrinsic_metadata
285 .variant
286 .fields
287 .iter()
288 .map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
289 let decoded =
290 scale_value::scale::decode_as_fields(bytes, &mut fields, self.metadata.types())?;
291
292 Ok(decoded)
293 }
294
295 pub fn as_extrinsic<E: StaticExtrinsic>(&self) -> Result<Option<E>, Error> {
298 let extrinsic_metadata = self.extrinsic_metadata()?;
299 if extrinsic_metadata.pallet.name() == E::PALLET
300 && extrinsic_metadata.variant.name == E::CALL
301 {
302 let mut fields = extrinsic_metadata
303 .variant
304 .fields
305 .iter()
306 .map(|f| scale_decode::Field::new(f.ty.id, f.name.as_deref()));
307 let decoded =
308 E::decode_as_fields(&mut self.field_bytes(), &mut fields, self.metadata.types())?;
309 Ok(Some(decoded))
310 } else {
311 Ok(None)
312 }
313 }
314
315 pub fn as_root_extrinsic<E: DecodeAsType>(&self) -> Result<E, Error> {
319 let decoded = E::decode_as_type(
320 &mut &self.call_bytes()[..],
321 self.metadata.outer_enums().call_enum_ty(),
322 self.metadata.types(),
323 )?;
324
325 Ok(decoded)
326 }
327
328 fn decoded_info(&self) -> &Extrinsic<'static, u32> {
329 &self.ext.0
330 }
331}
332
333pub struct FoundExtrinsic<T: Config, E> {
335 pub details: ExtrinsicDetails<T>,
337 pub value: E,
339}
340
341pub struct ExtrinsicMetadataDetails<'a> {
343 pub pallet: PalletMetadata<'a>,
345 pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use crate::config::SubstrateConfig;
353 use assert_matches::assert_matches;
354 use codec::{Decode, Encode};
355 use frame_metadata::v15::{CustomMetadata, OuterEnums};
356 use frame_metadata::{
357 v15::{ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, RuntimeMetadataV15},
358 RuntimeMetadataPrefixed,
359 };
360 use scale_info::{meta_type, TypeInfo};
361 use scale_value::Value;
362
363 #[allow(unused)]
367 #[derive(TypeInfo)]
368 struct ExtrinsicType<Address, Call, Signature, Extra> {
369 pub signature: Option<(Address, Signature, Extra)>,
370 pub function: Call,
371 }
372
373 #[allow(unused)]
376 #[derive(
377 Encode,
378 Decode,
379 TypeInfo,
380 Clone,
381 Debug,
382 PartialEq,
383 Eq,
384 scale_encode::EncodeAsType,
385 scale_decode::DecodeAsType,
386 )]
387 enum RuntimeCall {
388 Test(Pallet),
389 }
390
391 #[allow(unused)]
393 #[derive(
394 Encode,
395 Decode,
396 TypeInfo,
397 Clone,
398 Debug,
399 PartialEq,
400 Eq,
401 scale_encode::EncodeAsType,
402 scale_decode::DecodeAsType,
403 )]
404 enum Pallet {
405 #[allow(unused)]
406 #[codec(index = 2)]
407 TestCall {
408 value: u128,
409 signed: bool,
410 name: String,
411 },
412 }
413
414 #[allow(unused)]
415 #[derive(
416 Encode,
417 Decode,
418 TypeInfo,
419 Clone,
420 Debug,
421 PartialEq,
422 Eq,
423 scale_encode::EncodeAsType,
424 scale_decode::DecodeAsType,
425 )]
426 struct TestCallExtrinsic {
427 value: u128,
428 signed: bool,
429 name: String,
430 }
431
432 impl StaticExtrinsic for TestCallExtrinsic {
433 const PALLET: &'static str = "Test";
434 const CALL: &'static str = "TestCall";
435 }
436
437 fn metadata() -> Metadata {
439 let pallets = vec![PalletMetadata {
440 name: "Test",
441 storage: None,
442 calls: Some(PalletCallMetadata {
443 ty: meta_type::<Pallet>(),
444 }),
445 event: None,
446 constants: vec![],
447 error: None,
448 index: 0,
449 docs: vec![],
450 }];
451
452 let extrinsic = ExtrinsicMetadata {
453 version: 4,
454 signed_extensions: vec![],
455 address_ty: meta_type::<()>(),
456 call_ty: meta_type::<RuntimeCall>(),
457 signature_ty: meta_type::<()>(),
458 extra_ty: meta_type::<()>(),
459 };
460
461 let meta = RuntimeMetadataV15::new(
462 pallets,
463 extrinsic,
464 meta_type::<()>(),
465 vec![],
466 OuterEnums {
467 call_enum_ty: meta_type::<RuntimeCall>(),
468 event_enum_ty: meta_type::<()>(),
469 error_enum_ty: meta_type::<()>(),
470 },
471 CustomMetadata {
472 map: Default::default(),
473 },
474 );
475 let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
476 let metadata: subxt_metadata::Metadata = runtime_metadata.try_into().unwrap();
477
478 Metadata::from(metadata)
479 }
480
481 #[test]
482 fn extrinsic_metadata_consistency() {
483 let metadata = metadata();
484
485 let pallet = metadata.pallet_by_index(0).expect("pallet exists");
487 let extrinsic = pallet
488 .call_variant_by_index(2)
489 .expect("metadata contains the RuntimeCall enum with this pallet");
490
491 assert_eq!(pallet.name(), "Test");
492 assert_eq!(&extrinsic.name, "TestCall");
493 }
494
495 #[test]
496 fn insufficient_extrinsic_bytes() {
497 let metadata = metadata();
498
499 let result = Extrinsics::<SubstrateConfig>::decode_from(vec![vec![]], metadata);
501 assert_matches!(
502 result.err(),
503 Some(crate::Error::Block(
504 crate::error::BlockError::ExtrinsicDecodeError {
505 extrinsic_index: 0,
506 error: _
507 }
508 ))
509 );
510 }
511
512 #[test]
513 fn unsupported_version_extrinsic() {
514 use frame_decode::extrinsics::ExtrinsicDecodeError;
515
516 let metadata = metadata();
517
518 let result = Extrinsics::<SubstrateConfig>::decode_from(vec![vec![3u8].encode()], metadata);
520
521 assert_matches!(
522 result.err(),
523 Some(crate::Error::Block(
524 crate::error::BlockError::ExtrinsicDecodeError {
525 extrinsic_index: 0,
526 error: ExtrinsicDecodeError::VersionNotSupported(3),
527 }
528 ))
529 );
530 }
531
532 #[test]
533 fn tx_hashes_line_up() {
534 let metadata = metadata();
535
536 let tx = crate::dynamic::tx(
537 "Test",
538 "TestCall",
539 vec![
540 Value::u128(10),
541 Value::bool(true),
542 Value::string("SomeValue"),
543 ],
544 );
545
546 let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
548 .expect("Valid dynamic parameters are provided");
549
550 let extrinsics = Extrinsics::<SubstrateConfig>::decode_from(
552 vec![tx_encoded.encoded().to_owned()],
553 metadata,
554 )
555 .expect("Valid extrinsic");
556
557 let extrinsic = extrinsics.iter().next().unwrap();
558
559 assert_eq!(tx_encoded.encoded(), extrinsic.bytes(), "bytes should eq");
561 assert_eq!(tx_encoded.hash(), extrinsic.hash(), "hashes should eq");
563 }
564
565 #[test]
566 fn statically_decode_extrinsic() {
567 let metadata = metadata();
568
569 let tx = crate::dynamic::tx(
570 "Test",
571 "TestCall",
572 vec![
573 Value::u128(10),
574 Value::bool(true),
575 Value::string("SomeValue"),
576 ],
577 );
578 let tx_encoded = crate::tx::create_v4_unsigned::<SubstrateConfig, _>(&tx, &metadata)
579 .expect("Valid dynamic parameters are provided");
580
581 let extrinsics = Extrinsics::<SubstrateConfig>::decode_from(
584 vec![tx_encoded.encoded().to_owned()],
585 metadata,
586 )
587 .expect("Valid extrinsic");
588
589 let extrinsic = extrinsics.iter().next().unwrap();
590
591 assert!(!extrinsic.is_signed());
592
593 assert_eq!(extrinsic.index(), 0);
594
595 assert_eq!(extrinsic.pallet_index(), 0);
596 assert_eq!(
597 extrinsic
598 .pallet_name()
599 .expect("Valid metadata contains pallet name"),
600 "Test"
601 );
602
603 assert_eq!(extrinsic.variant_index(), 2);
604 assert_eq!(
605 extrinsic
606 .variant_name()
607 .expect("Valid metadata contains variant name"),
608 "TestCall"
609 );
610
611 let decoded_extrinsic = extrinsic
613 .as_root_extrinsic::<RuntimeCall>()
614 .expect("can decode extrinsic to root enum");
615
616 assert_eq!(
617 decoded_extrinsic,
618 RuntimeCall::Test(Pallet::TestCall {
619 value: 10,
620 signed: true,
621 name: "SomeValue".into(),
622 })
623 );
624
625 let decoded_extrinsic = extrinsic
627 .as_extrinsic::<TestCallExtrinsic>()
628 .expect("can decode extrinsic to extrinsic variant")
629 .expect("value cannot be None");
630
631 assert_eq!(
632 decoded_extrinsic,
633 TestCallExtrinsic {
634 value: 10,
635 signed: true,
636 name: "SomeValue".into(),
637 }
638 );
639 }
640}