Support extracting timestamps from blocks

This commit is contained in:
Luke Parker 2023-04-16 00:31:54 -04:00
parent 92ad689c7e
commit fa2cf03e61
No known key found for this signature in database
2 changed files with 69 additions and 4 deletions

View file

@ -1,6 +1,6 @@
use thiserror::Error; use thiserror::Error;
use scale::{Encode, Decode}; use scale::{Encode, Decode, Compact};
mod scale_value; mod scale_value;
pub(crate) use scale_value::{Value, Composite, scale_value, scale_composite}; pub(crate) use scale_value::{Value, Composite, scale_value, scale_composite};
@ -59,6 +59,14 @@ impl SubxtConfig for SeraiConfig {
#[derive(Debug)] #[derive(Debug)]
pub struct Block(ChainBlock<SeraiConfig>); pub struct Block(ChainBlock<SeraiConfig>);
impl Block { impl Block {
fn new(block: ChainBlock<SeraiConfig>) -> Result<Block, SeraiError> {
for extrinsic in &block.extrinsics {
if extrinsic.0.len() < 3 {
Err(SeraiError::InvalidNode)?;
}
}
Ok(Block(block))
}
pub fn hash(&self) -> [u8; 32] { pub fn hash(&self) -> [u8; 32] {
self.0.header.hash().into() self.0.header.hash().into()
} }
@ -66,6 +74,32 @@ impl Block {
self.0.header.number self.0.header.number
} }
/// Returns the time of this block, set by its producer, as a unix timestamp.
pub fn time(&self) -> Result<u64, SeraiError> {
for extrinsic in &self.0.extrinsics {
// Inherent/unsigned
let inherent = (extrinsic.0[0] >> 7) == 0;
// To timestamp pallet
use serai_runtime::Timestamp;
let timestamp =
extrinsic.0[1] == u8::try_from(PalletInfo::index::<Timestamp>().unwrap()).unwrap();
// set call
let set = extrinsic.0[2] == 0;
if inherent && timestamp && set {
if extrinsic.0.len() < 4 {
Err(SeraiError::InvalidNode)?;
}
return Ok(
Compact::<u64>::decode(&mut &extrinsic.0[3 ..]).map_err(|_| SeraiError::InvalidNode)?.0,
);
}
}
Err(SeraiError::InvalidNode)
}
pub fn header(&self) -> &Header { pub fn header(&self) -> &Header {
&self.0.header &self.0.header
} }
@ -146,7 +180,7 @@ impl Serai {
} }
pub async fn get_latest_block(&self) -> Result<Block, SeraiError> { pub async fn get_latest_block(&self) -> Result<Block, SeraiError> {
Ok(Block( Block::new(
self self
.0 .0
.rpc() .rpc()
@ -155,7 +189,7 @@ impl Serai {
.map_err(SeraiError::RpcError)? .map_err(SeraiError::RpcError)?
.ok_or(SeraiError::InvalidNode)? .ok_or(SeraiError::InvalidNode)?
.block, .block,
)) )
} }
// There is no provided method for this // There is no provided method for this
@ -207,7 +241,7 @@ impl Serai {
return Ok(None); return Ok(None);
} }
Ok(Some(Block(res.block))) Ok(Some(Block::new(res.block)?))
} }
// Ideally, this would be get_block_hash, not get_block_by_number // Ideally, this would be get_block_hash, not get_block_by_number

View file

@ -0,0 +1,31 @@
use std::time::{Duration, SystemTime};
use tokio::time::sleep;
use serai_client::Serai;
mod common;
use common::serai;
serai_test!(
async fn time() {
let serai = serai().await;
let mut number = serai.get_latest_block().await.unwrap().number();
let mut done = 0;
while done < 3 {
// Wait for the next block
let block = serai.get_latest_block().await.unwrap();
if block.number() == number {
sleep(Duration::from_secs(1)).await;
continue;
}
number = block.number();
// Make sure the time we extract from the block is within 5 seconds of now
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
assert!(now.saturating_sub(block.time().unwrap()) < 5);
done += 1;
}
}
);