use async_trait::async_trait;
use fuel_core_storage::{
not_found,
tables::Messages,
transactional::Transactional,
Error as StorageError,
Mappable,
Result as StorageResult,
StorageAsMut,
StorageAsRef,
StorageMutate,
};
use fuel_core_types::{
blockchain::primitives::DaBlockHeight,
entities::message::CheckedMessage,
};
#[cfg(test)]
mod tests;
#[async_trait]
pub trait RelayerDb: Send + Sync {
fn insert_messages(
&mut self,
da_height: &DaBlockHeight,
messages: &[CheckedMessage],
) -> StorageResult<()>;
fn set_finalized_da_height_to_at_least(
&mut self,
block: &DaBlockHeight,
) -> StorageResult<()>;
fn get_finalized_da_height(&self) -> StorageResult<DaBlockHeight>;
}
impl<T, Storage> RelayerDb for T
where
T: Send + Sync,
T: Transactional<Storage = Storage>,
T: StorageMutate<RelayerMetadata, Error = StorageError>,
Storage: StorageMutate<Messages, Error = StorageError>
+ StorageMutate<RelayerMetadata, Error = StorageError>,
{
fn insert_messages(
&mut self,
da_height: &DaBlockHeight,
messages: &[CheckedMessage],
) -> StorageResult<()> {
let mut db_tx = self.transaction();
let db = db_tx.as_mut();
let mut max_height = None;
for message in messages {
db.storage::<Messages>()
.insert(message.id(), message.message())?;
let max = max_height.get_or_insert(0u64);
*max = (*max).max(message.message().da_height.0);
}
if let Some(height) = max_height {
if **da_height < height {
return Err(anyhow::anyhow!("Invalid da height").into())
}
}
grow_monotonically(db, da_height)?;
db_tx.commit()?;
Ok(())
}
fn set_finalized_da_height_to_at_least(
&mut self,
height: &DaBlockHeight,
) -> StorageResult<()> {
let mut db_tx = self.transaction();
let db = db_tx.as_mut();
grow_monotonically(db, height)?;
db_tx.commit()?;
Ok(())
}
fn get_finalized_da_height(&self) -> StorageResult<DaBlockHeight> {
Ok(*StorageAsRef::storage::<RelayerMetadata>(&self)
.get(&METADATA_KEY)?
.ok_or(not_found!("DaBlockHeight missing for relayer"))?)
}
}
fn grow_monotonically<Storage>(
s: &mut Storage,
height: &DaBlockHeight,
) -> StorageResult<()>
where
Storage: StorageMutate<RelayerMetadata, Error = StorageError>,
{
let current = (&s)
.storage::<RelayerMetadata>()
.get(&METADATA_KEY)?
.map(|cow| cow.as_u64());
match current {
Some(current) => {
if **height > current {
s.storage::<RelayerMetadata>()
.insert(&METADATA_KEY, height)?;
}
}
None => {
s.storage::<RelayerMetadata>()
.insert(&METADATA_KEY, height)?;
}
}
Ok(())
}
pub struct RelayerMetadata;
impl Mappable for RelayerMetadata {
type Key = Self::OwnedKey;
type OwnedKey = ();
type Value = Self::OwnedValue;
type OwnedValue = DaBlockHeight;
}
const METADATA_KEY: () = ();