Cache the block's events within TemporalSerai

Event retrieval was prior:
- Retrieve all events in the block, which may be hundreds of KB
- Filter to just a few

Since it's frequent to want multiple sets of events, each filtered in their own
way, this caused the retrieval to happen multiple times. Now, it only will
happen once.

Also has the scoped clients take a reference, not an owned TemporalSerai.
This commit is contained in:
Luke Parker
2023-12-08 08:41:14 -05:00
parent 397fca748f
commit 7122e0faf4
12 changed files with 357 additions and 51 deletions

View File

@@ -11,18 +11,18 @@ const PALLET: &str = "Coins";
pub type CoinsEvent = serai_abi::coins::Event;
#[derive(Clone, Copy)]
pub struct SeraiCoins<'a>(pub(crate) TemporalSerai<'a>);
pub struct SeraiCoins<'a>(pub(crate) &'a TemporalSerai<'a>);
impl<'a> SeraiCoins<'a> {
pub fn into_inner(self) -> TemporalSerai<'a> {
self.0
}
pub async fn mint_events(&self) -> Result<Vec<CoinsEvent>, SeraiError> {
self
.0
.events(|event| {
if let serai_abi::Event::Coins(event) = event {
Some(event).filter(|event| matches!(event, CoinsEvent::Mint { .. }))
if matches!(event, CoinsEvent::Mint { .. }) {
Some(event.clone())
} else {
None
}
} else {
None
}
@@ -35,7 +35,11 @@ impl<'a> SeraiCoins<'a> {
.0
.events(|event| {
if let serai_abi::Event::Coins(event) = event {
Some(event).filter(|event| matches!(event, CoinsEvent::BurnWithInstruction { .. }))
if matches!(event, CoinsEvent::BurnWithInstruction { .. }) {
Some(event.clone())
} else {
None
}
} else {
None
}

View File

@@ -6,12 +6,14 @@ use crate::{SeraiError, TemporalSerai};
pub type DexEvent = serai_abi::dex::Event;
#[derive(Clone, Copy)]
pub struct SeraiDex<'a>(pub(crate) TemporalSerai<'a>);
pub struct SeraiDex<'a>(pub(crate) &'a TemporalSerai<'a>);
impl<'a> SeraiDex<'a> {
pub async fn events(&self) -> Result<Vec<DexEvent>, SeraiError> {
self
.0
.events(|event| if let serai_abi::Event::Dex(event) = event { Some(event) } else { None })
.events(
|event| if let serai_abi::Event::Dex(event) = event { Some(event.clone()) } else { None },
)
.await
}

View File

@@ -11,12 +11,8 @@ pub type InInstructionsEvent = serai_abi::in_instructions::Event;
const PALLET: &str = "InInstructions";
#[derive(Clone, Copy)]
pub struct SeraiInInstructions<'a>(pub(crate) TemporalSerai<'a>);
pub struct SeraiInInstructions<'a>(pub(crate) &'a TemporalSerai<'a>);
impl<'a> SeraiInInstructions<'a> {
pub fn into_inner(self) -> TemporalSerai<'a> {
self.0
}
pub async fn latest_block_for_network(
&self,
network: NetworkId,
@@ -36,7 +32,11 @@ impl<'a> SeraiInInstructions<'a> {
.0
.events(|event| {
if let serai_abi::Event::InInstructions(event) = event {
Some(event).filter(|event| matches!(event, InInstructionsEvent::Batch { .. }))
if matches!(event, InInstructionsEvent::Batch { .. }) {
Some(event.clone())
} else {
None
}
} else {
None
}

View File

@@ -1,5 +1,6 @@
use thiserror::Error;
use async_lock::RwLock;
use simple_request::{hyper, Request, Client};
use scale::{Encode, Decode, Compact};
@@ -69,8 +70,17 @@ pub struct Serai {
genesis: [u8; 32],
}
#[derive(Clone, Copy)]
pub struct TemporalSerai<'a>(pub(crate) &'a Serai, pub(crate) [u8; 32]);
type EventsInBlock = Vec<frame_system::EventRecord<Event, [u8; 32]>>;
pub struct TemporalSerai<'a> {
serai: &'a Serai,
block: [u8; 32],
events: RwLock<Option<EventsInBlock>>,
}
impl<'a> Clone for TemporalSerai<'a> {
fn clone(&self) -> Self {
Self { serai: self.serai, block: self.block, events: RwLock::new(None) }
}
}
impl Serai {
pub async fn call<Req: Serialize, Res: DeserializeOwned>(
@@ -289,27 +299,35 @@ impl Serai {
/// itself.
pub async fn as_of_latest_finalized_block(&self) -> Result<TemporalSerai, SeraiError> {
let latest = self.latest_finalized_block_hash().await?;
Ok(TemporalSerai(self, latest))
Ok(TemporalSerai { serai: self, block: latest, events: RwLock::new(None) })
}
/// Returns a TemporalSerai able to retrieve state as of the specified block.
pub fn as_of(&self, block: [u8; 32]) -> TemporalSerai {
TemporalSerai(self, block)
TemporalSerai { serai: self, block, events: RwLock::new(None) }
}
}
impl<'a> TemporalSerai<'a> {
pub fn into_inner(&self) -> &Serai {
self.0
}
async fn events<E>(
&self,
filter_map: impl Fn(&Event) -> Option<E>,
) -> Result<Vec<E>, SeraiError> {
let mut events = self.events.read().await;
if events.is_none() {
drop(events);
let mut events_write = self.events.write().await;
#[allow(clippy::unwrap_or_default)]
if events_write.is_none() {
*events_write = Some(self.storage("System", "Events", ()).await?.unwrap_or(vec![]));
}
drop(events_write);
events = self.events.read().await;
}
async fn events<E>(&self, filter_map: impl Fn(Event) -> Option<E>) -> Result<Vec<E>, SeraiError> {
let mut res = vec![];
let all_events: Option<Vec<frame_system::EventRecord<Event, [u8; 32]>>> =
self.storage("System", "Events", ()).await?;
#[allow(clippy::unwrap_or_default)]
for event in all_events.unwrap_or(vec![]) {
if let Some(event) = filter_map(event.event) {
for event in events.as_ref().unwrap() {
if let Some(event) = filter_map(&event.event) {
res.push(event);
}
}
@@ -328,7 +346,7 @@ impl<'a> TemporalSerai<'a> {
full_key.extend(key.encode());
let res: Option<String> =
self.0.call("state_getStorage", [hex::encode(full_key), hex::encode(self.1)]).await?;
self.serai.call("state_getStorage", [hex::encode(full_key), hex::encode(self.block)]).await?;
let Some(res) = res else { return Ok(None) };
let res = Serai::hex_decode(res)?;
Ok(Some(R::decode(&mut res.as_slice()).map_err(|_| {
@@ -336,19 +354,19 @@ impl<'a> TemporalSerai<'a> {
})?))
}
pub fn coins(self) -> SeraiCoins<'a> {
pub fn coins(&'a self) -> SeraiCoins<'a> {
SeraiCoins(self)
}
pub fn dex(self) -> SeraiDex<'a> {
pub fn dex(&'a self) -> SeraiDex<'a> {
SeraiDex(self)
}
pub fn in_instructions(self) -> SeraiInInstructions<'a> {
pub fn in_instructions(&'a self) -> SeraiInInstructions<'a> {
SeraiInInstructions(self)
}
pub fn validator_sets(self) -> SeraiValidatorSets<'a> {
pub fn validator_sets(&'a self) -> SeraiValidatorSets<'a> {
SeraiValidatorSets(self)
}
}

View File

@@ -16,18 +16,18 @@ const PALLET: &str = "ValidatorSets";
pub type ValidatorSetsEvent = serai_abi::validator_sets::Event;
#[derive(Clone, Copy)]
pub struct SeraiValidatorSets<'a>(pub(crate) TemporalSerai<'a>);
pub struct SeraiValidatorSets<'a>(pub(crate) &'a TemporalSerai<'a>);
impl<'a> SeraiValidatorSets<'a> {
pub fn into_inner(self) -> TemporalSerai<'a> {
self.0
}
pub async fn new_set_events(&self) -> Result<Vec<ValidatorSetsEvent>, SeraiError> {
self
.0
.events(|event| {
if let serai_abi::Event::ValidatorSets(event) = event {
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::NewSet { .. }))
if matches!(event, ValidatorSetsEvent::NewSet { .. }) {
Some(event.clone())
} else {
None
}
} else {
None
}
@@ -40,7 +40,11 @@ impl<'a> SeraiValidatorSets<'a> {
.0
.events(|event| {
if let serai_abi::Event::ValidatorSets(event) = event {
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::KeyGen { .. }))
if matches!(event, ValidatorSetsEvent::KeyGen { .. }) {
Some(event.clone())
} else {
None
}
} else {
None
}
@@ -53,7 +57,11 @@ impl<'a> SeraiValidatorSets<'a> {
.0
.events(|event| {
if let serai_abi::Event::ValidatorSets(event) = event {
Some(event).filter(|event| matches!(event, ValidatorSetsEvent::SetRetired { .. }))
if matches!(event, ValidatorSetsEvent::SetRetired { .. }) {
Some(event.clone())
} else {
None
}
} else {
None
}