use crate::ports::{
DatabaseTransaction,
RelayerDb,
Transactional,
};
use fuel_core_storage::{
blueprint::plain::Plain,
codec::{
postcard::Postcard,
primitive::Primitive,
},
kv_store::StorageColumn,
structured_storage::TableWithBlueprint,
transactional::{
Modifiable,
StorageTransaction,
},
Error as StorageError,
Mappable,
Result as StorageResult,
StorageAsMut,
StorageInspect,
StorageMutate,
};
use fuel_core_types::{
blockchain::primitives::DaBlockHeight,
services::relayer::Event,
};
#[repr(u32)]
#[derive(
Copy,
Clone,
Debug,
strum_macros::EnumCount,
strum_macros::IntoStaticStr,
PartialEq,
Eq,
enum_iterator::Sequence,
Hash,
)]
pub enum Column {
Metadata = 0,
History = 1,
RelayerHeight = 2,
}
impl Column {
pub const COUNT: usize = <Self as strum::EnumCount>::COUNT;
pub fn as_u32(&self) -> u32 {
*self as u32
}
}
impl StorageColumn for Column {
fn name(&self) -> &'static str {
self.into()
}
fn id(&self) -> u32 {
self.as_u32()
}
}
pub struct DaHeightTable;
impl Mappable for DaHeightTable {
type Key = Self::OwnedKey;
type OwnedKey = ();
type Value = Self::OwnedValue;
type OwnedValue = DaBlockHeight;
}
const METADATA_KEY: () = ();
impl TableWithBlueprint for DaHeightTable {
type Blueprint = Plain<Postcard, Primitive<8>>;
type Column = Column;
fn column() -> Column {
Column::RelayerHeight
}
}
pub struct EventsHistory;
impl Mappable for EventsHistory {
type Key = Self::OwnedKey;
type OwnedKey = DaBlockHeight;
type Value = [Event];
type OwnedValue = Vec<Event>;
}
impl TableWithBlueprint for EventsHistory {
type Blueprint = Plain<Primitive<8>, Postcard>;
type Column = Column;
fn column() -> Column {
Column::History
}
}
impl<T> RelayerDb for T
where
T: Send + Sync,
T: Transactional,
T: StorageInspect<DaHeightTable, Error = StorageError>,
for<'a> T::Transaction<'a>: StorageMutate<EventsHistory, Error = StorageError>
+ StorageMutate<DaHeightTable, Error = StorageError>,
{
fn insert_events(
&mut self,
da_height: &DaBlockHeight,
events: &[Event],
) -> StorageResult<()> {
let mut db_tx = self.transaction();
for event in events {
if da_height != &event.da_height() {
return Err(anyhow::anyhow!("Invalid da height").into())
}
}
db_tx.storage::<EventsHistory>().insert(da_height, events)?;
grow_monotonically(&mut db_tx, 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();
grow_monotonically(&mut db_tx, height)?;
db_tx.commit()?;
Ok(())
}
fn get_finalized_da_height(&self) -> StorageResult<DaBlockHeight> {
use fuel_core_storage::StorageAsRef;
Ok(*self
.storage::<DaHeightTable>()
.get(&METADATA_KEY)?
.unwrap_or_default())
}
}
impl<S> DatabaseTransaction for StorageTransaction<S>
where
S: Modifiable,
{
fn commit(self) -> StorageResult<()> {
self.commit()?;
Ok(())
}
}
fn grow_monotonically<Storage>(
s: &mut Storage,
height: &DaBlockHeight,
) -> StorageResult<()>
where
Storage: StorageMutate<DaHeightTable, Error = StorageError>,
{
let current = (&s)
.storage::<DaHeightTable>()
.get(&METADATA_KEY)?
.map(|cow| cow.as_u64());
match current {
Some(current) => {
if **height > current {
s.storage::<DaHeightTable>().insert(&METADATA_KEY, height)?;
}
}
None => {
s.storage::<DaHeightTable>().insert(&METADATA_KEY, height)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fuel_core_storage::basic_storage_tests!(
DaHeightTable,
<DaHeightTable as Mappable>::Key::default(),
<DaHeightTable as Mappable>::Value::default()
);
fuel_core_storage::basic_storage_tests!(
EventsHistory,
<EventsHistory as Mappable>::Key::default(),
vec![
Event::Message(Default::default()),
Event::Transaction(Default::default())
]
);
}