fuel_core_importer/
ports.rsuse fuel_core_storage::{
column::Column,
kv_store::KeyValueInspect,
tables::{
merkle::{
DenseMetadataKey,
FuelBlockMerkleMetadata,
},
FuelBlocks,
SealedBlockConsensus,
Transactions,
},
transactional::{
Changes,
ConflictPolicy,
Modifiable,
StorageTransaction,
WriteTransaction,
},
MerkleRoot,
Result as StorageResult,
StorageAsMut,
StorageAsRef,
};
use fuel_core_types::{
blockchain::{
block::Block,
consensus::Consensus,
SealedBlock,
},
fuel_tx::UniqueIdentifier,
fuel_types::{
BlockHeight,
ChainId,
},
services::executor::{
Result as ExecutorResult,
UncommittedValidationResult,
},
};
#[cfg_attr(test, mockall::automock(type Database = crate::importer::test::MockDatabase;))]
pub trait Validator: Send + Sync {
fn validate(
&self,
block: &Block,
) -> ExecutorResult<UncommittedValidationResult<Changes>>;
}
pub trait Transactional {
type Transaction<'a>: DatabaseTransaction
where
Self: 'a;
fn storage_transaction(&mut self, changes: Changes) -> Self::Transaction<'_>;
}
pub trait ImporterDatabase: Send + Sync {
fn latest_block_height(&self) -> StorageResult<Option<BlockHeight>>;
fn latest_block_root(&self) -> StorageResult<Option<MerkleRoot>>;
}
#[cfg_attr(test, mockall::automock)]
pub trait DatabaseTransaction {
fn latest_block_root(&self) -> StorageResult<Option<MerkleRoot>>;
fn store_new_block(
&mut self,
chain_id: &ChainId,
block: &SealedBlock,
) -> StorageResult<bool>;
fn commit(self) -> StorageResult<()>;
}
#[cfg_attr(test, mockall::automock)]
pub trait BlockVerifier: Send + Sync {
fn verify_block_fields(
&self,
consensus: &Consensus,
block: &Block,
) -> anyhow::Result<()>;
}
impl<S> Transactional for S
where
S: KeyValueInspect<Column = Column> + Modifiable,
{
type Transaction<'a> = StorageTransaction<&'a mut S> where Self: 'a;
fn storage_transaction(&mut self, changes: Changes) -> Self::Transaction<'_> {
self.write_transaction()
.with_changes(changes)
.with_policy(ConflictPolicy::Fail)
}
}
impl<S> DatabaseTransaction for StorageTransaction<S>
where
S: KeyValueInspect<Column = Column> + Modifiable,
{
fn latest_block_root(&self) -> StorageResult<Option<MerkleRoot>> {
Ok(self
.storage_as_ref::<FuelBlockMerkleMetadata>()
.get(&DenseMetadataKey::Latest)?
.map(|cow| *cow.root()))
}
fn store_new_block(
&mut self,
chain_id: &ChainId,
block: &SealedBlock,
) -> StorageResult<bool> {
let mut storage = self.write_transaction();
let height = block.entity.header().height();
let mut found = storage
.storage_as_mut::<FuelBlocks>()
.replace(height, &block.entity.compress(chain_id))?
.is_some();
found |= storage
.storage_as_mut::<SealedBlockConsensus>()
.replace(height, &block.consensus)?
.is_some();
for tx in block.entity.transactions() {
found |= storage
.storage_as_mut::<Transactions>()
.replace(&tx.id(chain_id), tx)?
.is_some();
}
storage.commit()?;
Ok(!found)
}
fn commit(self) -> StorageResult<()> {
self.commit()?;
Ok(())
}
}