use crate::{
backend::BlockRef,
blocks::Extrinsics,
client::{OfflineClientT, OnlineClientT},
config::{Config, HashFor, Header},
error::{AccountNonceError, BlockError, EventsError, ExtrinsicError},
events,
runtime_api::RuntimeApi,
storage::StorageClientAt,
};
use codec::{Decode, Encode};
use futures::lock::Mutex as AsyncMutex;
use std::sync::Arc;
pub struct Block<T: Config, C> {
header: T::Header,
block_ref: BlockRef<HashFor<T>>,
client: C,
cached_events: CachedEvents<T>,
}
impl<T: Config, C: Clone> Clone for Block<T, C> {
fn clone(&self) -> Self {
Self {
header: self.header.clone(),
block_ref: self.block_ref.clone(),
client: self.client.clone(),
cached_events: self.cached_events.clone(),
}
}
}
pub(crate) type CachedEvents<T> = Arc<AsyncMutex<Option<events::Events<T>>>>;
impl<T, C> Block<T, C>
where
T: Config,
C: OfflineClientT<T>,
{
pub(crate) fn new(header: T::Header, block_ref: BlockRef<HashFor<T>>, client: C) -> Self {
Block { header, block_ref, client, cached_events: Default::default() }
}
pub fn reference(&self) -> BlockRef<HashFor<T>> {
self.block_ref.clone()
}
pub fn hash(&self) -> HashFor<T> {
self.block_ref.hash()
}
pub fn number(&self) -> <T::Header as crate::config::Header>::Number {
self.header().number()
}
pub fn header(&self) -> &T::Header {
&self.header
}
}
impl<T, C> Block<T, C>
where
T: Config,
C: OnlineClientT<T>,
{
pub async fn events(&self) -> Result<events::Events<T>, EventsError> {
get_events(&self.client, self.hash(), &self.cached_events).await
}
pub async fn extrinsics(&self) -> Result<Extrinsics<T, C>, ExtrinsicError> {
let block_hash = self.hash();
let extrinsics = self
.client
.backend()
.block_body(block_hash)
.await
.map_err(ExtrinsicError::CannotGetBlockBody)?
.ok_or_else(|| ExtrinsicError::BlockNotFound(block_hash.into()))?;
let extrinsics = Extrinsics::new(
self.client.clone(),
extrinsics,
self.cached_events.clone(),
block_hash,
)?;
Ok(extrinsics)
}
pub fn storage(&self) -> StorageClientAt<T, C> {
StorageClientAt::new(self.client.clone(), self.block_ref.clone())
}
pub async fn runtime_api(&self) -> RuntimeApi<T, C> {
RuntimeApi::new(self.client.clone(), self.block_ref.clone())
}
pub async fn account_nonce(&self, account_id: &T::AccountId) -> Result<u64, BlockError> {
get_account_nonce(&self.client, account_id, self.hash()).await.map_err(|e| {
BlockError::AccountNonceError {
block_hash: self.hash().into(),
account_id: account_id.encode().into(),
reason: e,
}
})
}
}
pub(crate) async fn get_events<C, T>(
client: &C,
block_hash: HashFor<T>,
cached_events: &AsyncMutex<Option<events::Events<T>>>,
) -> Result<events::Events<T>, EventsError>
where
T: Config,
C: OnlineClientT<T>,
{
let mut lock = cached_events.lock().await;
let events = match &*lock {
Some(events) => events.clone(),
None => {
let events = events::EventsClient::new(client.clone()).at(block_hash).await?;
lock.replace(events.clone());
events
},
};
Ok(events)
}
pub(crate) async fn get_account_nonce<C, T>(
client: &C,
account_id: &T::AccountId,
block_hash: HashFor<T>,
) -> Result<u64, AccountNonceError>
where
C: OnlineClientT<T>,
T: Config,
{
let account_nonce_bytes = client
.backend()
.call("AccountNonceApi_account_nonce", Some(&account_id.encode()), block_hash)
.await?;
let cursor = &mut &account_nonce_bytes[..];
let account_nonce: u64 = match account_nonce_bytes.len() {
2 => u16::decode(cursor)?.into(),
4 => u32::decode(cursor)?.into(),
8 => u64::decode(cursor)?,
_ => {
return Err(AccountNonceError::WrongNumberOfBytes(account_nonce_bytes.len()));
},
};
Ok(account_nonce)
}