use core::fmt;
use crate::{
chain_index::types::TransactionHash, error::FinalisedStateError, read_fixed_le, read_u32_le,
read_u8, version, write_fixed_le, write_u32_le, write_u8, BlockHash, BlockHeaderData,
CommitmentTreeData, CompactBlockStream, FixedEncodedLen, Height, IndexedBlock,
OrchardCompactTx, OrchardTxList, SaplingCompactTx, SaplingTxList, StatusType,
TransparentCompactTx, TransparentTxList, TxLocation, TxidList, ZainoVersionedSerde,
};
#[cfg(feature = "transparent_address_history_experimental")]
use crate::{chain_index::types::AddrEventBytes, AddrScript, Outpoint};
use async_trait::async_trait;
use bitflags::bitflags;
use corez::io::{self, Read, Write};
use zaino_proto::proto::utils::PoolTypeFilter;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, Default)]
pub(crate) struct Capability: u32 {
const READ_CORE = 0b0000_0001;
const WRITE_CORE = 0b0000_0010;
const BLOCK_CORE_EXT = 0b0000_0100;
const BLOCK_TRANSPARENT_EXT = 0b0000_1000;
const BLOCK_SHIELDED_EXT = 0b0001_0000;
const COMPACT_BLOCK_EXT = 0b0010_0000;
const CHAIN_BLOCK_EXT = 0b0100_0000;
#[cfg(feature = "transparent_address_history_experimental")]
const TRANSPARENT_HIST_EXT = 0b1000_0000;
}
}
impl Capability {
pub(crate) const LATEST: Capability = {
let base = Capability::READ_CORE
.union(Capability::WRITE_CORE)
.union(Capability::BLOCK_CORE_EXT)
.union(Capability::BLOCK_TRANSPARENT_EXT)
.union(Capability::BLOCK_SHIELDED_EXT)
.union(Capability::COMPACT_BLOCK_EXT)
.union(Capability::CHAIN_BLOCK_EXT);
#[cfg(feature = "transparent_address_history_experimental")]
{
base.union(Capability::TRANSPARENT_HIST_EXT)
}
#[cfg(not(feature = "transparent_address_history_experimental"))]
{
base
}
};
#[inline]
pub(crate) const fn has(self, other: Capability) -> bool {
self.contains(other)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum CapabilityRequest {
ReadCore,
WriteCore,
BlockCoreExt,
BlockTransparentExt,
BlockShieldedExt,
CompactBlockExt,
IndexedBlockExt,
#[cfg(feature = "transparent_address_history_experimental")]
TransparentHistExt,
}
impl CapabilityRequest {
#[inline]
pub(crate) const fn as_capability(self) -> Capability {
match self {
CapabilityRequest::ReadCore => Capability::READ_CORE,
CapabilityRequest::WriteCore => Capability::WRITE_CORE,
CapabilityRequest::BlockCoreExt => Capability::BLOCK_CORE_EXT,
CapabilityRequest::BlockTransparentExt => Capability::BLOCK_TRANSPARENT_EXT,
CapabilityRequest::BlockShieldedExt => Capability::BLOCK_SHIELDED_EXT,
CapabilityRequest::CompactBlockExt => Capability::COMPACT_BLOCK_EXT,
CapabilityRequest::IndexedBlockExt => Capability::CHAIN_BLOCK_EXT,
#[cfg(feature = "transparent_address_history_experimental")]
CapabilityRequest::TransparentHistExt => Capability::TRANSPARENT_HIST_EXT,
}
}
#[inline]
pub(crate) const fn name(self) -> &'static str {
match self {
CapabilityRequest::ReadCore => "READ_CORE",
CapabilityRequest::WriteCore => "WRITE_CORE",
CapabilityRequest::BlockCoreExt => "BLOCK_CORE_EXT",
CapabilityRequest::BlockTransparentExt => "BLOCK_TRANSPARENT_EXT",
CapabilityRequest::BlockShieldedExt => "BLOCK_SHIELDED_EXT",
CapabilityRequest::CompactBlockExt => "COMPACT_BLOCK_EXT",
CapabilityRequest::IndexedBlockExt => "CHAIN_BLOCK_EXT",
#[cfg(feature = "transparent_address_history_experimental")]
CapabilityRequest::TransparentHistExt => "TRANSPARENT_HIST_EXT",
}
}
}
impl From<CapabilityRequest> for Capability {
#[inline]
fn from(req: CapabilityRequest) -> Self {
req.as_capability()
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, Default)]
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct DbMetadata {
pub(crate) version: DbVersion,
pub(crate) schema_hash: [u8; 32],
pub(crate) migration_status: MigrationStatus,
}
impl DbMetadata {
pub(crate) fn new(
version: DbVersion,
schema_hash: [u8; 32],
migration_status: MigrationStatus,
) -> Self {
Self {
version,
schema_hash,
migration_status,
}
}
pub(crate) fn version(&self) -> DbVersion {
self.version
}
pub(crate) fn schema(&self) -> [u8; 32] {
self.schema_hash
}
pub(crate) fn migration_status(&self) -> MigrationStatus {
self.migration_status
}
}
impl ZainoVersionedSerde for DbMetadata {
const VERSION: u8 = version::V1;
fn encode_latest<W: Write>(&self, w: &mut W) -> io::Result<()> {
Self::encode_v1(self, w)
}
fn decode_latest<R: Read>(r: &mut R) -> io::Result<Self> {
Self::decode_v1(r)
}
fn encode_v1<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.version.serialize_with_version(&mut *w, 1)?;
write_fixed_le::<32, _>(&mut *w, &self.schema_hash)?;
self.migration_status.serialize_with_version(&mut *w, 1)
}
fn decode_v1<R: Read>(r: &mut R) -> io::Result<Self> {
let version = DbVersion::deserialize(&mut *r)?;
let schema_hash = read_fixed_le::<32, _>(&mut *r)?;
let migration_status = MigrationStatus::deserialize(&mut *r)?;
Ok(DbMetadata {
version,
schema_hash,
migration_status,
})
}
}
impl FixedEncodedLen for DbMetadata {
const ENCODED_LEN: usize = DbVersion::VERSIONED_LEN + 32 + MigrationStatus::VERSIONED_LEN;
}
impl core::fmt::Display for DbMetadata {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"DbMetadata {{ version: {}.{}.{} , schema_hash: 0x",
self.version.major(),
self.version.minor(),
self.version.patch()
)?;
for byte in &self.schema_hash[..4] {
write!(f, "{byte:02x}")?;
}
write!(f, "… }}")
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, Default)]
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct DbVersion {
pub(crate) major: u32,
pub(crate) minor: u32,
pub(crate) patch: u32,
}
impl DbVersion {
pub(crate) fn new(major: u32, minor: u32, patch: u32) -> Self {
Self {
major,
minor,
patch,
}
}
pub(crate) fn major(&self) -> u32 {
self.major
}
pub(crate) fn minor(&self) -> u32 {
self.minor
}
pub(crate) fn patch(&self) -> u32 {
self.patch
}
pub(crate) fn capability(&self) -> Capability {
match (self.major, self.minor) {
(0, _) => {
Capability::READ_CORE | Capability::WRITE_CORE | Capability::COMPACT_BLOCK_EXT
}
(1, 0) => {
let base = Capability::READ_CORE
| Capability::WRITE_CORE
| Capability::BLOCK_CORE_EXT
| Capability::BLOCK_TRANSPARENT_EXT
| Capability::BLOCK_SHIELDED_EXT
| Capability::COMPACT_BLOCK_EXT
| Capability::CHAIN_BLOCK_EXT;
#[cfg(feature = "transparent_address_history_experimental")]
{
base | Capability::TRANSPARENT_HIST_EXT
}
#[cfg(not(feature = "transparent_address_history_experimental"))]
{
base
}
}
_ => Capability::empty(),
}
}
}
impl ZainoVersionedSerde for DbVersion {
const VERSION: u8 = version::V1;
fn encode_latest<W: Write>(&self, w: &mut W) -> io::Result<()> {
Self::encode_v1(self, w)
}
fn decode_latest<R: Read>(r: &mut R) -> io::Result<Self> {
Self::decode_v1(r)
}
fn encode_v1<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_u32_le(&mut *w, self.major)?;
write_u32_le(&mut *w, self.minor)?;
write_u32_le(&mut *w, self.patch)
}
fn decode_v1<R: Read>(r: &mut R) -> io::Result<Self> {
let major = read_u32_le(&mut *r)?;
let minor = read_u32_le(&mut *r)?;
let patch = read_u32_le(&mut *r)?;
Ok(DbVersion {
major,
minor,
patch,
})
}
}
impl FixedEncodedLen for DbVersion {
const ENCODED_LEN: usize = 4 + 4 + 4;
}
impl core::fmt::Display for DbVersion {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash)]
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub(crate) enum MigrationStatus {
#[default]
Empty,
PartialBuidInProgress,
PartialBuildComplete,
FinalBuildInProgress,
Complete,
}
impl fmt::Display for MigrationStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let status_str = match self {
MigrationStatus::Empty => "Empty",
MigrationStatus::PartialBuidInProgress => "Partial build in progress",
MigrationStatus::PartialBuildComplete => "Partial build complete",
MigrationStatus::FinalBuildInProgress => "Final build in progress",
MigrationStatus::Complete => "Complete",
};
write!(f, "{status_str}")
}
}
impl ZainoVersionedSerde for MigrationStatus {
const VERSION: u8 = version::V1;
fn encode_latest<W: Write>(&self, w: &mut W) -> io::Result<()> {
Self::encode_v1(self, w)
}
fn decode_latest<R: Read>(r: &mut R) -> io::Result<Self> {
Self::decode_v1(r)
}
fn encode_v1<W: Write>(&self, w: &mut W) -> io::Result<()> {
let tag = match self {
MigrationStatus::Empty => 0,
MigrationStatus::PartialBuidInProgress => 1,
MigrationStatus::PartialBuildComplete => 2,
MigrationStatus::FinalBuildInProgress => 3,
MigrationStatus::Complete => 4,
};
write_u8(w, tag)
}
fn decode_v1<R: Read>(r: &mut R) -> io::Result<Self> {
match read_u8(r)? {
0 => Ok(MigrationStatus::Empty),
1 => Ok(MigrationStatus::PartialBuidInProgress),
2 => Ok(MigrationStatus::PartialBuildComplete),
3 => Ok(MigrationStatus::FinalBuildInProgress),
4 => Ok(MigrationStatus::Complete),
other => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid MigrationStatus tag: {other}"),
)),
}
}
}
impl FixedEncodedLen for MigrationStatus {
const ENCODED_LEN: usize = 1;
}
#[async_trait]
pub trait DbRead: Send + Sync {
async fn db_height(&self) -> Result<Option<Height>, FinalisedStateError>;
async fn get_block_height(
&self,
hash: BlockHash,
) -> Result<Option<Height>, FinalisedStateError>;
async fn get_block_hash(
&self,
height: Height,
) -> Result<Option<BlockHash>, FinalisedStateError>;
async fn get_metadata(&self) -> Result<DbMetadata, FinalisedStateError>;
}
#[async_trait]
pub trait DbWrite: Send + Sync {
async fn write_block(&self, block: IndexedBlock) -> Result<(), FinalisedStateError>;
async fn delete_block_at_height(&self, height: Height) -> Result<(), FinalisedStateError>;
async fn delete_block(&self, block: &IndexedBlock) -> Result<(), FinalisedStateError>;
async fn update_metadata(&self, metadata: DbMetadata) -> Result<(), FinalisedStateError>;
}
#[async_trait]
pub trait DbCore: DbRead + DbWrite + Send + Sync {
fn status(&self) -> StatusType;
async fn shutdown(&self) -> Result<(), FinalisedStateError>;
}
#[async_trait]
pub trait BlockCoreExt: Send + Sync {
async fn get_block_header(
&self,
height: Height,
) -> Result<BlockHeaderData, FinalisedStateError>;
async fn get_block_range_headers(
&self,
start: Height,
end: Height,
) -> Result<Vec<BlockHeaderData>, FinalisedStateError>;
async fn get_block_txids(&self, height: Height) -> Result<TxidList, FinalisedStateError>;
async fn get_block_range_txids(
&self,
start: Height,
end: Height,
) -> Result<Vec<TxidList>, FinalisedStateError>;
async fn get_txid(
&self,
tx_location: TxLocation,
) -> Result<TransactionHash, FinalisedStateError>;
async fn get_tx_location(
&self,
txid: &TransactionHash,
) -> Result<Option<TxLocation>, FinalisedStateError>;
}
#[async_trait]
pub trait BlockTransparentExt: Send + Sync {
async fn get_transparent(
&self,
tx_location: TxLocation,
) -> Result<Option<TransparentCompactTx>, FinalisedStateError>;
async fn get_block_transparent(
&self,
height: Height,
) -> Result<TransparentTxList, FinalisedStateError>;
async fn get_block_range_transparent(
&self,
start: Height,
end: Height,
) -> Result<Vec<TransparentTxList>, FinalisedStateError>;
}
#[async_trait]
pub trait BlockShieldedExt: Send + Sync {
async fn get_sapling(
&self,
tx_location: TxLocation,
) -> Result<Option<SaplingCompactTx>, FinalisedStateError>;
async fn get_block_sapling(&self, height: Height)
-> Result<SaplingTxList, FinalisedStateError>;
async fn get_block_range_sapling(
&self,
start: Height,
end: Height,
) -> Result<Vec<SaplingTxList>, FinalisedStateError>;
async fn get_orchard(
&self,
tx_location: TxLocation,
) -> Result<Option<OrchardCompactTx>, FinalisedStateError>;
async fn get_block_orchard(&self, height: Height)
-> Result<OrchardTxList, FinalisedStateError>;
async fn get_block_range_orchard(
&self,
start: Height,
end: Height,
) -> Result<Vec<OrchardTxList>, FinalisedStateError>;
async fn get_block_commitment_tree_data(
&self,
height: Height,
) -> Result<CommitmentTreeData, FinalisedStateError>;
async fn get_block_range_commitment_tree_data(
&self,
start: Height,
end: Height,
) -> Result<Vec<CommitmentTreeData>, FinalisedStateError>;
}
#[async_trait]
pub trait CompactBlockExt: Send + Sync {
async fn get_compact_block(
&self,
height: Height,
pool_types: PoolTypeFilter,
) -> Result<zaino_proto::proto::compact_formats::CompactBlock, FinalisedStateError>;
async fn get_compact_block_stream(
&self,
start_height: Height,
end_height: Height,
pool_types: PoolTypeFilter,
) -> Result<CompactBlockStream, FinalisedStateError>;
}
#[async_trait]
pub trait IndexedBlockExt: Send + Sync {
async fn get_chain_block(
&self,
height: Height,
) -> Result<Option<IndexedBlock>, FinalisedStateError>;
}
#[cfg(feature = "transparent_address_history_experimental")]
#[async_trait]
pub trait TransparentHistExt: Send + Sync {
async fn addr_records(
&self,
addr_script: AddrScript,
) -> Result<Option<Vec<AddrEventBytes>>, FinalisedStateError>;
async fn addr_and_index_records(
&self,
addr_script: AddrScript,
tx_location: TxLocation,
) -> Result<Option<Vec<AddrEventBytes>>, FinalisedStateError>;
async fn addr_tx_locations_by_range(
&self,
addr_script: AddrScript,
start_height: Height,
end_height: Height,
) -> Result<Option<Vec<TxLocation>>, FinalisedStateError>;
async fn addr_utxos_by_range(
&self,
addr_script: AddrScript,
start_height: Height,
end_height: Height,
) -> Result<Option<Vec<(TxLocation, u16, u64)>>, FinalisedStateError>;
async fn addr_balance_by_range(
&self,
addr_script: AddrScript,
start_height: Height,
end_height: Height,
) -> Result<i64, FinalisedStateError>;
async fn get_outpoint_spender(
&self,
outpoint: Outpoint,
) -> Result<Option<TxLocation>, FinalisedStateError>;
async fn get_outpoint_spenders(
&self,
outpoints: Vec<Outpoint>,
) -> Result<Vec<Option<TxLocation>>, FinalisedStateError>;
}