use crate::{
database::transaction::DatabaseTransaction,
state::{
in_memory::memory_store::MemoryStore,
DataSource,
IterDirection,
},
};
use fuel_core_chain_config::{
ChainConfigDb,
CoinConfig,
ContractConfig,
MessageConfig,
};
use fuel_core_storage::{
transactional::{
StorageTransaction,
Transactional,
},
Result as StorageResult,
};
use fuel_core_types::blockchain::primitives::BlockHeight;
use serde::{
de::DeserializeOwned,
Serialize,
};
use std::{
fmt::{
self,
Debug,
Formatter,
},
marker::Send,
sync::Arc,
};
pub use fuel_core_database::Error;
pub type Result<T> = core::result::Result<T, Error>;
type DatabaseError = Error;
type DatabaseResult<T> = Result<T>;
#[cfg(feature = "rocksdb")]
use crate::state::rocks_db::RocksDb;
#[cfg(feature = "rocksdb")]
use std::path::Path;
use strum::EnumCount;
#[cfg(feature = "rocksdb")]
use tempfile::TempDir;
mod block;
mod code_root;
mod contracts;
mod message;
mod receipts;
#[cfg(feature = "relayer")]
mod relayer;
mod sealed_block;
mod state;
pub(crate) mod coin;
pub mod balances;
pub mod metadata;
pub mod storage;
pub mod transaction;
pub mod transactions;
pub mod vm_database;
#[repr(u32)]
#[derive(
Copy, Clone, Debug, strum_macros::EnumCount, PartialEq, Eq, enum_iterator::Sequence,
)]
pub enum Column {
Metadata = 0,
ContractsRawCode = 1,
ContractsInfo = 2,
ContractsState = 3,
ContractsLatestUtxo = 4,
ContractsAssets = 5,
Coins = 6,
OwnedCoins = 7,
Transactions = 8,
TransactionStatus = 9,
TransactionsByOwnerBlockIdx = 10,
Receipts = 11,
FuelBlocks = 12,
FuelBlockSecondaryKeyBlockHeights = 13,
Messages = 14,
OwnedMessageIds = 15,
FuelBlockConsensus = 16,
FuelBlockMerkleData = 17,
FuelBlockMerkleMetadata = 18,
SpentMessages = 19,
RelayerMetadata = 20,
}
impl Column {
pub const COUNT: usize = <Self as EnumCount>::COUNT;
pub fn as_usize(&self) -> usize {
*self as usize
}
}
#[derive(Clone, Debug)]
pub struct Database {
data: DataSource,
_drop: Arc<DropResources>,
}
trait DropFnTrait: FnOnce() {}
impl<F> DropFnTrait for F where F: FnOnce() {}
type DropFn = Box<dyn DropFnTrait>;
impl fmt::Debug for DropFn {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "DropFn")
}
}
#[derive(Debug, Default)]
struct DropResources {
drop: Option<DropFn>,
}
impl<F: 'static + FnOnce()> From<F> for DropResources {
fn from(closure: F) -> Self {
Self {
drop: Option::Some(Box::new(closure)),
}
}
}
impl Drop for DropResources {
fn drop(&mut self) {
if let Some(drop) = self.drop.take() {
(drop)()
}
}
}
unsafe impl Send for Database {}
unsafe impl Sync for Database {}
impl Database {
pub fn new(data_source: DataSource) -> Self {
Self {
data: data_source,
_drop: Default::default(),
}
}
#[cfg(feature = "rocksdb")]
pub fn open(path: &Path, capacity: impl Into<Option<usize>>) -> DatabaseResult<Self> {
use anyhow::Context;
let db = RocksDb::default_open(path, capacity.into()).context("Failed to open rocksdb, you may need to wipe a pre-existing incompatible db `rm -rf ~/.fuel/db`")?;
Ok(Database {
data: Arc::new(db),
_drop: Default::default(),
})
}
pub fn in_memory() -> Self {
Self {
data: Arc::new(MemoryStore::default()),
_drop: Default::default(),
}
}
fn insert<K: AsRef<[u8]>, V: Serialize, R: DeserializeOwned>(
&self,
key: K,
column: Column,
value: &V,
) -> DatabaseResult<Option<R>> {
let result = self.data.put(
key.as_ref(),
column,
Arc::new(postcard::to_stdvec(value).map_err(|_| DatabaseError::Codec)?),
)?;
if let Some(previous) = result {
Ok(Some(
postcard::from_bytes(&previous).map_err(|_| DatabaseError::Codec)?,
))
} else {
Ok(None)
}
}
fn remove<V: DeserializeOwned>(
&self,
key: &[u8],
column: Column,
) -> DatabaseResult<Option<V>> {
self.data
.delete(key, column)?
.map(|val| postcard::from_bytes(&val).map_err(|_| DatabaseError::Codec))
.transpose()
}
fn get<V: DeserializeOwned>(
&self,
key: &[u8],
column: Column,
) -> DatabaseResult<Option<V>> {
self.data
.get(key, column)?
.map(|val| postcard::from_bytes(&val).map_err(|_| DatabaseError::Codec))
.transpose()
}
fn contains_key(&self, key: &[u8], column: Column) -> DatabaseResult<bool> {
self.data.exists(key, column)
}
fn iter_all<K, V>(
&self,
column: Column,
direction: Option<IterDirection>,
) -> impl Iterator<Item = DatabaseResult<(K, V)>> + '_
where
K: From<Vec<u8>>,
V: DeserializeOwned,
{
self.iter_all_filtered::<K, V, Vec<u8>, Vec<u8>>(column, None, None, direction)
}
fn iter_all_by_prefix<K, V, P>(
&self,
column: Column,
prefix: Option<P>,
) -> impl Iterator<Item = DatabaseResult<(K, V)>> + '_
where
K: From<Vec<u8>>,
V: DeserializeOwned,
P: AsRef<[u8]>,
{
self.iter_all_filtered::<K, V, P, [u8; 0]>(column, prefix, None, None)
}
fn iter_all_by_start<K, V, S>(
&self,
column: Column,
start: Option<S>,
direction: Option<IterDirection>,
) -> impl Iterator<Item = DatabaseResult<(K, V)>> + '_
where
K: From<Vec<u8>>,
V: DeserializeOwned,
S: AsRef<[u8]>,
{
self.iter_all_filtered::<K, V, [u8; 0], S>(column, None, start, direction)
}
fn iter_all_filtered<K, V, P, S>(
&self,
column: Column,
prefix: Option<P>,
start: Option<S>,
direction: Option<IterDirection>,
) -> impl Iterator<Item = DatabaseResult<(K, V)>> + '_
where
K: From<Vec<u8>>,
V: DeserializeOwned,
P: AsRef<[u8]>,
S: AsRef<[u8]>,
{
self.data
.iter_all(
column,
prefix.as_ref().map(|p| p.as_ref()),
start.as_ref().map(|s| s.as_ref()),
direction.unwrap_or_default(),
)
.map(|val| {
val.and_then(|(key, value)| {
let key = K::from(key);
let value: V =
postcard::from_bytes(&value).map_err(|_| DatabaseError::Codec)?;
Ok((key, value))
})
})
}
pub fn transaction(&self) -> DatabaseTransaction {
self.into()
}
}
impl Transactional for Database {
type Storage = Database;
fn transaction(&self) -> StorageTransaction<Database> {
StorageTransaction::new(self.transaction())
}
}
impl AsRef<Database> for Database {
fn as_ref(&self) -> &Database {
self
}
}
impl Default for Database {
fn default() -> Self {
#[cfg(not(feature = "rocksdb"))]
{
Self {
data: Arc::new(MemoryStore::default()),
_drop: Default::default(),
}
}
#[cfg(feature = "rocksdb")]
{
let tmp_dir = TempDir::new().unwrap();
let db = RocksDb::default_open(tmp_dir.path(), None).unwrap();
Self {
data: Arc::new(db),
_drop: Arc::new(
{
move || {
drop(tmp_dir);
}
}
.into(),
),
}
}
}
}
impl ChainConfigDb for Database {
fn get_coin_config(&self) -> StorageResult<Option<Vec<CoinConfig>>> {
Self::get_coin_config(self).map_err(Into::into)
}
fn get_contract_config(&self) -> StorageResult<Option<Vec<ContractConfig>>> {
Self::get_contract_config(self)
}
fn get_message_config(&self) -> StorageResult<Option<Vec<MessageConfig>>> {
Self::get_message_config(self).map_err(Into::into)
}
fn get_block_height(&self) -> StorageResult<BlockHeight> {
Self::latest_height(self)
}
}
#[test]
fn column_keys_not_exceed_count() {
use enum_iterator::all;
for column in all::<Column>() {
assert!(column.as_usize() < Column::COUNT);
}
}