use diesel::query_dsl::methods::SelectDsl;
use diesel::{
ExpressionMethods,
OptionalExtension,
QueryDsl,
Queryable,
QueryableByName,
RunQueryDsl,
Selectable,
SelectableHelper,
SqliteConnection,
};
use miden_node_utils::limiter::{QueryParamBlockLimit, QueryParamLimiter};
use miden_protocol::block::{BlockHeader, BlockNumber, BlockSignatures};
use super::DatabaseError;
use crate::db::models::conv::SqlTypeConvert;
use crate::db::models::vec_raw_try_into;
use crate::db::{BlockHeaderCommitment, schema};
pub(crate) fn select_block_header_by_block_num(
conn: &mut SqliteConnection,
maybe_block_num: Option<BlockNumber>,
) -> Result<Option<BlockHeader>, DatabaseError> {
let sel = SelectDsl::select(schema::block_headers::table, BlockHeaderRawRow::as_select());
let row = if let Some(block_num) = maybe_block_num {
sel.filter(schema::block_headers::block_num.eq(block_num.to_raw_sql()))
.get_result::<BlockHeaderRawRow>(conn)
.optional()?
} else {
sel.order(schema::block_headers::block_num.desc())
.limit(1)
.get_result::<BlockHeaderRawRow>(conn)
.optional()?
};
row.map(std::convert::TryInto::try_into).transpose()
}
pub(crate) fn select_block_header_and_signatures_by_block_num(
conn: &mut SqliteConnection,
block_number: BlockNumber,
) -> Result<Option<(BlockHeader, BlockSignatures)>, DatabaseError> {
let sel = SelectDsl::select(schema::block_headers::table, BlockHeaderRawRow::as_select());
let row = sel
.filter(schema::block_headers::block_num.eq(block_number.to_raw_sql()))
.get_result::<BlockHeaderRawRow>(conn)
.optional()?;
row.map(std::convert::TryInto::try_into).transpose()
}
pub fn select_block_headers(
conn: &mut SqliteConnection,
blocks: impl Iterator<Item = BlockNumber> + Send,
) -> Result<Vec<BlockHeader>, DatabaseError> {
QueryParamBlockLimit::check(blocks.size_hint().0)?;
let blocks = blocks.map(SqlTypeConvert::to_raw_sql).collect::<Vec<_>>();
let raw_block_headers =
QueryDsl::select(schema::block_headers::table, BlockHeaderRawRow::as_select())
.filter(schema::block_headers::block_num.eq_any(blocks))
.load::<BlockHeaderRawRow>(conn)?;
vec_raw_try_into(raw_block_headers)
}
pub fn select_all_block_header_commitments(
conn: &mut SqliteConnection,
) -> Result<Vec<BlockHeaderCommitment>, DatabaseError> {
let raw_commitments =
QueryDsl::select(schema::block_headers::table, schema::block_headers::commitment)
.order(schema::block_headers::block_num.asc())
.load::<Vec<u8>>(conn)?;
let commitments = raw_commitments
.into_iter()
.map(BlockHeaderCommitment::from_raw_sql)
.collect::<Result<_, _>>()?;
Ok(commitments)
}
#[derive(Debug, Clone, Queryable, QueryableByName, Selectable)]
#[diesel(table_name = schema::block_headers)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
pub struct BlockHeaderRawRow {
#[expect(dead_code)]
pub block_num: i64,
pub block_header: Vec<u8>,
pub signature: Vec<u8>,
pub commitment: Vec<u8>,
}
impl TryInto<BlockHeader> for BlockHeaderRawRow {
type Error = DatabaseError;
fn try_into(self) -> Result<BlockHeader, Self::Error> {
let block_header = BlockHeader::from_raw_sql(self.block_header)?;
debug_assert_eq!(
BlockHeaderCommitment::new(&block_header),
BlockHeaderCommitment::from_raw_sql(self.commitment)
.expect("Database always contains valid format commitments")
);
Ok(block_header)
}
}
impl TryInto<(BlockHeader, BlockSignatures)> for BlockHeaderRawRow {
type Error = DatabaseError;
fn try_into(self) -> Result<(BlockHeader, BlockSignatures), Self::Error> {
let block_header = miden_node_persistence::decode::<BlockHeader>(&self.block_header[..])?;
let signatures = miden_node_persistence::decode::<BlockSignatures>(&self.signature[..])?;
Ok((block_header, signatures))
}
}