subxt/blocks/
block_types.rs

1// Copyright 2019-2025 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    backend::BlockRef,
7    blocks::Extrinsics,
8    client::{OfflineClientT, OnlineClientT},
9    config::{Config, HashFor, Header},
10    error::{BlockError, DecodeError, Error},
11    events,
12    runtime_api::RuntimeApi,
13    storage::Storage,
14};
15
16use codec::{Decode, Encode};
17use futures::lock::Mutex as AsyncMutex;
18use std::sync::Arc;
19
20/// A representation of a block.
21pub struct Block<T: Config, C> {
22    header: T::Header,
23    block_ref: BlockRef<HashFor<T>>,
24    client: C,
25    // Since we obtain the same events for every extrinsic, let's
26    // cache them so that we only ever do that once:
27    cached_events: CachedEvents<T>,
28}
29
30impl<T: Config, C: Clone> Clone for Block<T, C> {
31    fn clone(&self) -> Self {
32        Self {
33            header: self.header.clone(),
34            block_ref: self.block_ref.clone(),
35            client: self.client.clone(),
36            cached_events: self.cached_events.clone(),
37        }
38    }
39}
40
41// A cache for our events so we don't fetch them more than once when
42// iterating over events for extrinsics.
43pub(crate) type CachedEvents<T> = Arc<AsyncMutex<Option<events::Events<T>>>>;
44
45impl<T, C> Block<T, C>
46where
47    T: Config,
48    C: OfflineClientT<T>,
49{
50    pub(crate) fn new(header: T::Header, block_ref: BlockRef<HashFor<T>>, client: C) -> Self {
51        Block {
52            header,
53            block_ref,
54            client,
55            cached_events: Default::default(),
56        }
57    }
58
59    /// Return a reference to the given block. While this reference is kept alive,
60    /// the backend will (if possible) endeavour to keep hold of the block.
61    pub fn reference(&self) -> BlockRef<HashFor<T>> {
62        self.block_ref.clone()
63    }
64
65    /// Return the block hash.
66    pub fn hash(&self) -> HashFor<T> {
67        self.block_ref.hash()
68    }
69
70    /// Return the block number.
71    pub fn number(&self) -> <T::Header as crate::config::Header>::Number {
72        self.header().number()
73    }
74
75    /// Return the entire block header.
76    pub fn header(&self) -> &T::Header {
77        &self.header
78    }
79}
80
81impl<T, C> Block<T, C>
82where
83    T: Config,
84    C: OnlineClientT<T>,
85{
86    /// Return the events associated with the block, fetching them from the node if necessary.
87    pub async fn events(&self) -> Result<events::Events<T>, Error> {
88        get_events(&self.client, self.hash(), &self.cached_events).await
89    }
90
91    /// Fetch and return the extrinsics in the block body.
92    pub async fn extrinsics(&self) -> Result<Extrinsics<T, C>, Error> {
93        let block_hash = self.hash();
94        let Some(extrinsics) = self.client.backend().block_body(block_hash).await? else {
95            return Err(BlockError::not_found(block_hash).into());
96        };
97
98        Extrinsics::new(
99            self.client.clone(),
100            extrinsics,
101            self.cached_events.clone(),
102            block_hash,
103        )
104    }
105
106    /// Work with storage.
107    pub fn storage(&self) -> Storage<T, C> {
108        Storage::new(self.client.clone(), self.block_ref.clone())
109    }
110
111    /// Execute a runtime API call at this block.
112    pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
113        Ok(RuntimeApi::new(self.client.clone(), self.block_ref.clone()))
114    }
115
116    /// Get the account nonce for a given account ID at this block.
117    pub async fn account_nonce(&self, account_id: &T::AccountId) -> Result<u64, Error> {
118        get_account_nonce(&self.client, account_id, self.hash()).await
119    }
120}
121
122// Return Events from the cache, or fetch from the node if needed.
123pub(crate) async fn get_events<C, T>(
124    client: &C,
125    block_hash: HashFor<T>,
126    cached_events: &AsyncMutex<Option<events::Events<T>>>,
127) -> Result<events::Events<T>, Error>
128where
129    T: Config,
130    C: OnlineClientT<T>,
131{
132    // Acquire lock on the events cache. We either get back our events or we fetch and set them
133    // before unlocking, so only one fetch call should ever be made. We do this because the
134    // same events can be shared across all extrinsics in the block.
135    let mut lock = cached_events.lock().await;
136    let events = match &*lock {
137        Some(events) => events.clone(),
138        None => {
139            let events = events::EventsClient::new(client.clone())
140                .at(block_hash)
141                .await?;
142            lock.replace(events.clone());
143            events
144        }
145    };
146
147    Ok(events)
148}
149
150// Return the account nonce at some block hash for an account ID.
151pub(crate) async fn get_account_nonce<C, T>(
152    client: &C,
153    account_id: &T::AccountId,
154    block_hash: HashFor<T>,
155) -> Result<u64, Error>
156where
157    C: OnlineClientT<T>,
158    T: Config,
159{
160    let account_nonce_bytes = client
161        .backend()
162        .call(
163            "AccountNonceApi_account_nonce",
164            Some(&account_id.encode()),
165            block_hash,
166        )
167        .await?;
168
169    // custom decoding from a u16/u32/u64 into a u64, based on the number of bytes we got back.
170    let cursor = &mut &account_nonce_bytes[..];
171    let account_nonce: u64 = match account_nonce_bytes.len() {
172        2 => u16::decode(cursor)?.into(),
173        4 => u32::decode(cursor)?.into(),
174        8 => u64::decode(cursor)?,
175        _ => {
176            return Err(Error::Decode(DecodeError::custom_string(format!(
177                "state call AccountNonceApi_account_nonce returned an unexpected number of bytes: {} (expected 2, 4 or 8)",
178                account_nonce_bytes.len()
179            ))));
180        }
181    };
182    Ok(account_nonce)
183}