use fuel_core_storage::{
MerkleRoot,
Result as StorageResult,
StorageAsMut,
StorageAsRef,
column::Column,
kv_store::KeyValueInspect,
tables::{
FuelBlocks,
SealedBlockConsensus,
Transactions,
merkle::{
DenseMetadataKey,
FuelBlockMerkleMetadata,
},
},
transactional::{
Changes,
ConflictPolicy,
ReadTransaction,
StorageChanges,
StorageTransaction,
WriteTransaction,
},
};
use fuel_core_types::{
blockchain::{
SealedBlock,
block::Block,
consensus::Consensus,
},
fuel_tx::UniqueIdentifier,
fuel_types::{
BlockHeight,
ChainId,
},
services::executor::{
Result as ExecutorResult,
UncommittedValidationResult,
},
};
#[cfg_attr(any(test, feature = "test-helpers"), mockall::automock)]
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(&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>>;
fn commit_changes(&mut self, changes: StorageChanges) -> StorageResult<()>;
}
#[cfg_attr(any(test, feature = "test-helpers"), 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 into_changes(self) -> Changes;
}
#[cfg_attr(any(test, feature = "test-helpers"), mockall::automock)]
pub trait BlockVerifier: Send + Sync {
fn verify_block_fields(
&self,
consensus: &Consensus,
block: &Block,
) -> anyhow::Result<()>;
}
pub trait BlockReconciliationWritePort: Send + Sync {
fn publish_produced_block(&self, block: &SealedBlock) -> anyhow::Result<()>;
}
impl<S> Transactional for S
where
S: KeyValueInspect<Column = Column>,
{
type Transaction<'a>
= StorageTransaction<&'a S>
where
Self: 'a;
fn storage_transaction(&self, changes: Changes) -> Self::Transaction<'_> {
self.read_transaction()
.with_changes(changes)
.with_policy(ConflictPolicy::Fail)
}
}
impl<S> DatabaseTransaction for StorageTransaction<S>
where
S: KeyValueInspect<Column = Column>,
{
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 into_changes(self) -> Changes {
self.into_changes()
}
}