gear_subxt/blocks/
block_types.rs

1// Copyright 2019-2023 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5use crate::{
6    blocks::{extrinsic_types::ExtrinsicPartTypeIds, Extrinsics},
7    client::{OfflineClientT, OnlineClientT},
8    config::{Config, Header},
9    error::{BlockError, Error},
10    events,
11    rpc::types::ChainBlockResponse,
12    runtime_api::RuntimeApi,
13    storage::Storage,
14};
15
16use futures::lock::Mutex as AsyncMutex;
17use std::sync::Arc;
18
19/// A representation of a block.
20pub struct Block<T: Config, C> {
21    header: T::Header,
22    client: C,
23    // Since we obtain the same events for every extrinsic, let's
24    // cache them so that we only ever do that once:
25    cached_events: CachedEvents<T>,
26}
27
28// A cache for our events so we don't fetch them more than once when
29// iterating over events for extrinsics.
30pub(crate) type CachedEvents<T> = Arc<AsyncMutex<Option<events::Events<T>>>>;
31
32impl<T, C> Block<T, C>
33where
34    T: Config,
35    C: OfflineClientT<T>,
36{
37    pub(crate) fn new(header: T::Header, client: C) -> Self {
38        Block {
39            header,
40            client,
41            cached_events: Default::default(),
42        }
43    }
44
45    /// Return the block hash.
46    pub fn hash(&self) -> T::Hash {
47        self.header.hash()
48    }
49
50    /// Return the block number.
51    pub fn number(&self) -> <T::Header as crate::config::Header>::Number {
52        self.header().number()
53    }
54
55    /// Return the entire block header.
56    pub fn header(&self) -> &T::Header {
57        &self.header
58    }
59}
60
61impl<T, C> Block<T, C>
62where
63    T: Config,
64    C: OnlineClientT<T>,
65{
66    /// Return the events associated with the block, fetching them from the node if necessary.
67    pub async fn events(&self) -> Result<events::Events<T>, Error> {
68        get_events(&self.client, self.header.hash(), &self.cached_events).await
69    }
70
71    /// Fetch and return the block body.
72    pub async fn body(&self) -> Result<BlockBody<T, C>, Error> {
73        let ids = ExtrinsicPartTypeIds::new(&self.client.metadata())?;
74        let block_hash = self.header.hash();
75        let Some(block_details) = self.client.rpc().block(Some(block_hash)).await? else {
76            return Err(BlockError::not_found(block_hash).into());
77        };
78
79        Ok(BlockBody::new(
80            self.client.clone(),
81            block_details,
82            self.cached_events.clone(),
83            ids,
84        ))
85    }
86
87    /// Work with storage.
88    pub fn storage(&self) -> Storage<T, C> {
89        let block_hash = self.hash();
90        Storage::new(self.client.clone(), block_hash)
91    }
92
93    /// Execute a runtime API call at this block.
94    pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
95        Ok(RuntimeApi::new(self.client.clone(), self.hash()))
96    }
97}
98
99/// The body of a block.
100pub struct BlockBody<T: Config, C> {
101    details: ChainBlockResponse<T>,
102    client: C,
103    cached_events: CachedEvents<T>,
104    ids: ExtrinsicPartTypeIds,
105}
106
107impl<T, C> BlockBody<T, C>
108where
109    T: Config,
110    C: OfflineClientT<T>,
111{
112    pub(crate) fn new(
113        client: C,
114        details: ChainBlockResponse<T>,
115        cached_events: CachedEvents<T>,
116        ids: ExtrinsicPartTypeIds,
117    ) -> Self {
118        Self {
119            details,
120            client,
121            cached_events,
122            ids,
123        }
124    }
125
126    /// Returns an iterator over the extrinsics in the block body.
127    // Dev note: The returned iterator is 'static + Send so that we can box it up and make
128    // use of it with our `FilterExtrinsic` stuff.
129    pub fn extrinsics(&self) -> Extrinsics<T, C> {
130        Extrinsics::new(
131            self.client.clone(),
132            self.details.block.extrinsics.clone(),
133            self.cached_events.clone(),
134            self.ids,
135            self.details.block.header.hash(),
136        )
137    }
138}
139
140// Return Events from the cache, or fetch from the node if needed.
141pub(crate) async fn get_events<C, T>(
142    client: &C,
143    block_hash: T::Hash,
144    cached_events: &AsyncMutex<Option<events::Events<T>>>,
145) -> Result<events::Events<T>, Error>
146where
147    T: Config,
148    C: OnlineClientT<T>,
149{
150    // Acquire lock on the events cache. We either get back our events or we fetch and set them
151    // before unlocking, so only one fetch call should ever be made. We do this because the
152    // same events can be shared across all extrinsics in the block.
153    let lock = cached_events.lock().await;
154    let events = match &*lock {
155        Some(events) => events.clone(),
156        None => {
157            events::EventsClient::new(client.clone())
158                .at(block_hash)
159                .await?
160        }
161    };
162
163    Ok(events)
164}