From fa2cf03e61c7fc82773396f40cc34f848b2ff86a Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 16 Apr 2023 00:31:54 -0400 Subject: [PATCH] Support extracting timestamps from blocks --- substrate/client/src/serai/mod.rs | 42 ++++++++++++++++++++++++++++--- substrate/client/tests/time.rs | 31 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 substrate/client/tests/time.rs diff --git a/substrate/client/src/serai/mod.rs b/substrate/client/src/serai/mod.rs index 6814a988..73abd1a7 100644 --- a/substrate/client/src/serai/mod.rs +++ b/substrate/client/src/serai/mod.rs @@ -1,6 +1,6 @@ use thiserror::Error; -use scale::{Encode, Decode}; +use scale::{Encode, Decode, Compact}; mod scale_value; pub(crate) use scale_value::{Value, Composite, scale_value, scale_composite}; @@ -59,6 +59,14 @@ impl SubxtConfig for SeraiConfig { #[derive(Debug)] pub struct Block(ChainBlock); impl Block { + fn new(block: ChainBlock) -> Result { + for extrinsic in &block.extrinsics { + if extrinsic.0.len() < 3 { + Err(SeraiError::InvalidNode)?; + } + } + Ok(Block(block)) + } pub fn hash(&self) -> [u8; 32] { self.0.header.hash().into() } @@ -66,6 +74,32 @@ impl Block { self.0.header.number } + /// Returns the time of this block, set by its producer, as a unix timestamp. + pub fn time(&self) -> Result { + 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::().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::::decode(&mut &extrinsic.0[3 ..]).map_err(|_| SeraiError::InvalidNode)?.0, + ); + } + } + Err(SeraiError::InvalidNode) + } + pub fn header(&self) -> &Header { &self.0.header } @@ -146,7 +180,7 @@ impl Serai { } pub async fn get_latest_block(&self) -> Result { - Ok(Block( + Block::new( self .0 .rpc() @@ -155,7 +189,7 @@ impl Serai { .map_err(SeraiError::RpcError)? .ok_or(SeraiError::InvalidNode)? .block, - )) + ) } // There is no provided method for this @@ -207,7 +241,7 @@ impl Serai { 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 diff --git a/substrate/client/tests/time.rs b/substrate/client/tests/time.rs new file mode 100644 index 00000000..5eac481d --- /dev/null +++ b/substrate/client/tests/time.rs @@ -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; + } + } +);