use crate::models::dht::{
DumpChainOpRow, K2ChainOpForWireRow, K2OpHashRow, K2OpIdSinceRow, K2OpPresentRow,
K2WarrantForWireRow,
};
use holo_hash::AgentPubKey;
#[cfg(any(test, feature = "inspection"))]
use holo_hash::{AnyLinkableHash, DhtOpHash};
use sqlx::{Executor, QueryBuilder, Sqlite};
#[derive(Debug, Clone, Copy)]
pub struct ArcBounds {
pub start: u32,
pub end: u32,
}
impl ArcBounds {
fn start_i64(self) -> i64 {
self.start as i64
}
fn end_i64(self) -> i64 {
self.end as i64
}
}
pub(crate) async fn op_hashes_in_time_slice<'e, E>(
executor: E,
arc: ArcBounds,
t_start_micros: i64,
t_end_micros: i64,
) -> sqlx::Result<Vec<K2OpHashRow>>
where
E: Executor<'e, Database = Sqlite>,
{
let sql = "
SELECT op_hash AS hash, basis_hash, serialized_size, sort_ts FROM (
SELECT
ChainOp.hash AS op_hash,
ChainOp.basis_hash AS basis_hash,
ChainOp.serialized_size AS serialized_size,
Action.timestamp AS sort_ts
FROM ChainOp
JOIN Action ON ChainOp.action_hash = Action.hash
WHERE
(
(? <= ? AND ChainOp.storage_center_loc >= ?
AND ChainOp.storage_center_loc <= ?)
OR
(? > ? AND (ChainOp.storage_center_loc <= ?
OR ChainOp.storage_center_loc >= ?))
)
AND Action.timestamp >= ?
AND Action.timestamp < ?
AND ChainOp.locally_validated = 1
UNION ALL
SELECT
Warrant.hash AS op_hash,
Warrant.warrantee AS basis_hash,
WarrantOp.serialized_size AS serialized_size,
Warrant.timestamp AS sort_ts
FROM Warrant
JOIN WarrantOp ON WarrantOp.hash = Warrant.hash
WHERE
(
(? <= ? AND WarrantOp.storage_center_loc >= ?
AND WarrantOp.storage_center_loc <= ?)
OR
(? > ? AND (WarrantOp.storage_center_loc <= ?
OR WarrantOp.storage_center_loc >= ?))
)
AND Warrant.timestamp >= ?
AND Warrant.timestamp < ?
)
ORDER BY sort_ts ASC
";
let s = arc.start_i64();
let e = arc.end_i64();
sqlx::query_as::<_, K2OpHashRow>(sql)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(e)
.bind(s)
.bind(t_start_micros)
.bind(t_end_micros)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(e)
.bind(s)
.bind(t_start_micros)
.bind(t_end_micros)
.fetch_all(executor)
.await
}
pub(crate) async fn op_ids_since_time_batch<'e, E>(
executor: E,
arc: ArcBounds,
t_min_micros: i64,
limit: u32,
) -> sqlx::Result<Vec<K2OpIdSinceRow>>
where
E: Executor<'e, Database = Sqlite>,
{
let sql = "
SELECT op_hash AS hash, basis_hash, when_integrated, serialized_size FROM (
SELECT
ChainOp.hash AS op_hash,
ChainOp.basis_hash AS basis_hash,
ChainOp.when_integrated AS when_integrated,
ChainOp.serialized_size AS serialized_size
FROM ChainOp
WHERE
(
(? <= ? AND ChainOp.storage_center_loc >= ?
AND ChainOp.storage_center_loc <= ?)
OR
(? > ? AND (ChainOp.storage_center_loc <= ?
OR ChainOp.storage_center_loc >= ?))
)
AND ChainOp.when_integrated >= ?
AND ChainOp.locally_validated = 1
UNION ALL
SELECT
Warrant.hash AS op_hash,
Warrant.warrantee AS basis_hash,
WarrantOp.when_integrated AS when_integrated,
WarrantOp.serialized_size AS serialized_size
FROM Warrant
JOIN WarrantOp ON WarrantOp.hash = Warrant.hash
WHERE
(
(? <= ? AND WarrantOp.storage_center_loc >= ?
AND WarrantOp.storage_center_loc <= ?)
OR
(? > ? AND (WarrantOp.storage_center_loc <= ?
OR WarrantOp.storage_center_loc >= ?))
)
AND WarrantOp.when_integrated >= ?
)
ORDER BY when_integrated ASC
LIMIT ?
";
let s = arc.start_i64();
let e = arc.end_i64();
sqlx::query_as::<_, K2OpIdSinceRow>(sql)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(e)
.bind(s)
.bind(t_min_micros)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(e)
.bind(s)
.bind(t_min_micros)
.bind(limit as i64)
.fetch_all(executor)
.await
}
pub(crate) async fn check_op_hashes_present<'e, E>(
executor: E,
hashes: &[Vec<u8>],
) -> sqlx::Result<Vec<K2OpPresentRow>>
where
E: Executor<'e, Database = Sqlite>,
{
if hashes.is_empty() {
return Ok(Vec::new());
}
let mut qb: QueryBuilder<Sqlite> =
QueryBuilder::new("SELECT hash, basis_hash FROM LimboChainOp WHERE hash IN (");
{
let mut sep = qb.separated(", ");
for h in hashes {
sep.push_bind(h);
}
}
qb.push(
") UNION
SELECT hash, basis_hash FROM ChainOp WHERE locally_validated = 1 AND hash IN (",
);
{
let mut sep = qb.separated(", ");
for h in hashes {
sep.push_bind(h);
}
}
qb.push(
") UNION
SELECT hash, warrantee AS basis_hash FROM Warrant WHERE hash IN (",
);
{
let mut sep = qb.separated(", ");
for h in hashes {
sep.push_bind(h);
}
}
qb.push(")");
qb.build_query_as::<K2OpPresentRow>()
.fetch_all(executor)
.await
}
pub(crate) async fn get_chain_ops_for_wire<'e, E>(
executor: E,
op_hashes: &[Vec<u8>],
) -> sqlx::Result<Vec<K2ChainOpForWireRow>>
where
E: Executor<'e, Database = Sqlite>,
{
if op_hashes.is_empty() {
return Ok(Vec::new());
}
let mut qb: QueryBuilder<Sqlite> = QueryBuilder::new(
"SELECT
ChainOp.hash AS op_hash,
ChainOp.basis_hash AS basis_hash,
ChainOp.op_type AS op_type,
Action.hash AS action_hash,
Action.author AS author,
Action.timestamp AS timestamp,
Action.seq AS seq,
Action.prev_hash AS prev_hash,
Action.action_data AS action_data,
Action.signature AS signature,
Entry.blob AS entry_blob
FROM ChainOp
JOIN Action ON ChainOp.action_hash = Action.hash
LEFT JOIN Entry ON Action.entry_hash = Entry.hash
WHERE ChainOp.locally_validated = 1
AND ChainOp.hash IN (",
);
{
let mut sep = qb.separated(", ");
for h in op_hashes {
sep.push_bind(h);
}
}
qb.push(")");
qb.build_query_as::<K2ChainOpForWireRow>()
.fetch_all(executor)
.await
}
pub(crate) async fn get_warrants_for_wire<'e, E>(
executor: E,
op_hashes: &[Vec<u8>],
) -> sqlx::Result<Vec<K2WarrantForWireRow>>
where
E: Executor<'e, Database = Sqlite>,
{
if op_hashes.is_empty() {
return Ok(Vec::new());
}
let mut qb: QueryBuilder<Sqlite> = QueryBuilder::new(
"SELECT Warrant.hash, Warrant.author, Warrant.timestamp,
Warrant.warrantee, Warrant.proof, Warrant.signature
FROM Warrant
JOIN WarrantOp ON WarrantOp.hash = Warrant.hash
WHERE Warrant.hash IN (",
);
{
let mut sep = qb.separated(", ");
for h in op_hashes {
sep.push_bind(h);
}
}
qb.push(")");
qb.build_query_as::<K2WarrantForWireRow>()
.fetch_all(executor)
.await
}
pub(crate) async fn integrated_chain_ops_for_dump<'e, E>(
executor: E,
after: Option<(i64, &[u8])>,
) -> sqlx::Result<Vec<DumpChainOpRow>>
where
E: Executor<'e, Database = Sqlite>,
{
let mut qb: QueryBuilder<Sqlite> = QueryBuilder::new(
"SELECT
ChainOp.hash AS op_hash,
ChainOp.basis_hash AS basis_hash,
ChainOp.op_type AS op_type,
ChainOp.when_integrated AS when_integrated,
Action.hash AS action_hash,
Action.author AS author,
Action.timestamp AS timestamp,
Action.seq AS seq,
Action.prev_hash AS prev_hash,
Action.action_data AS action_data,
Action.signature AS signature,
Entry.blob AS entry_blob
FROM ChainOp
JOIN Action ON ChainOp.action_hash = Action.hash
LEFT JOIN Entry ON Action.entry_hash = Entry.hash
WHERE ChainOp.locally_validated = 1",
);
if let Some((when_integrated, hash)) = after {
qb.push(" AND (ChainOp.when_integrated > ");
qb.push_bind(when_integrated);
qb.push(" OR (ChainOp.when_integrated = ");
qb.push_bind(when_integrated);
qb.push(" AND ChainOp.hash > ");
qb.push_bind(hash);
qb.push("))");
}
qb.push(" ORDER BY ChainOp.when_integrated ASC, ChainOp.hash ASC");
qb.build_query_as::<DumpChainOpRow>()
.fetch_all(executor)
.await
}
pub(crate) async fn ops_to_publish_for_wire<'e, E>(
executor: E,
author: &AgentPubKey,
) -> sqlx::Result<Vec<K2ChainOpForWireRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as::<_, K2ChainOpForWireRow>(
"SELECT
ChainOp.hash AS op_hash,
ChainOp.basis_hash AS basis_hash,
ChainOp.op_type AS op_type,
Action.hash AS action_hash,
Action.author AS author,
Action.timestamp AS timestamp,
Action.seq AS seq,
Action.prev_hash AS prev_hash,
Action.action_data AS action_data,
Action.signature AS signature,
Entry.blob AS entry_blob
FROM ChainOp
JOIN Action ON ChainOp.action_hash = Action.hash
LEFT JOIN Entry ON Action.entry_hash = Entry.hash
WHERE ChainOp.locally_validated = 1
AND Action.author = ?
AND (ChainOp.op_type != 2 OR Action.private_entry = 0)
ORDER BY ChainOp.hash ASC",
)
.bind(author.get_raw_36())
.fetch_all(executor)
.await
}
pub(crate) async fn limbo_chain_ops_for_dump<'e, E>(
executor: E,
ready: bool,
) -> sqlx::Result<Vec<K2ChainOpForWireRow>>
where
E: Executor<'e, Database = Sqlite>,
{
let predicate = if ready {
LIMBO_CHAIN_OP_READY_PRED.to_string()
} else {
limbo_chain_op_not_ready_pred()
};
let sql = format!(
"SELECT
LimboChainOp.hash AS op_hash,
LimboChainOp.basis_hash AS basis_hash,
LimboChainOp.op_type AS op_type,
Action.hash AS action_hash,
Action.author AS author,
Action.timestamp AS timestamp,
Action.seq AS seq,
Action.prev_hash AS prev_hash,
Action.action_data AS action_data,
Action.signature AS signature,
Entry.blob AS entry_blob
FROM LimboChainOp
JOIN Action ON LimboChainOp.action_hash = Action.hash
LEFT JOIN Entry ON Action.entry_hash = Entry.hash
WHERE {predicate}
ORDER BY LimboChainOp.hash ASC"
);
sqlx::query_as::<_, K2ChainOpForWireRow>(sqlx::AssertSqlSafe(sql))
.fetch_all(executor)
.await
}
pub(crate) async fn integrated_warrants_for_dump<'e, E>(
executor: E,
) -> sqlx::Result<Vec<K2WarrantForWireRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as::<_, K2WarrantForWireRow>(
"SELECT Warrant.hash, Warrant.author, Warrant.timestamp,
Warrant.warrantee, Warrant.proof, Warrant.signature
FROM Warrant
JOIN WarrantOp ON WarrantOp.hash = Warrant.hash
ORDER BY WarrantOp.hash ASC",
)
.fetch_all(executor)
.await
}
pub(crate) async fn earliest_authored_timestamp_in_arc<'e, E>(
executor: E,
arc: ArcBounds,
) -> sqlx::Result<Option<i64>>
where
E: Executor<'e, Database = Sqlite>,
{
let sql = "
SELECT MIN(ts) FROM (
SELECT Action.timestamp AS ts
FROM ChainOp
JOIN Action ON ChainOp.action_hash = Action.hash
WHERE
(
(? <= ? AND ChainOp.storage_center_loc >= ?
AND ChainOp.storage_center_loc <= ?)
OR
(? > ? AND (ChainOp.storage_center_loc <= ?
OR ChainOp.storage_center_loc >= ?))
)
AND ChainOp.locally_validated = 1
UNION ALL
SELECT Warrant.timestamp AS ts
FROM Warrant
JOIN WarrantOp ON WarrantOp.hash = Warrant.hash
WHERE
(
(? <= ? AND WarrantOp.storage_center_loc >= ?
AND WarrantOp.storage_center_loc <= ?)
OR
(? > ? AND (WarrantOp.storage_center_loc <= ?
OR WarrantOp.storage_center_loc >= ?))
)
)
";
let s = arc.start_i64();
let e = arc.end_i64();
let row: Option<(Option<i64>,)> = sqlx::query_as(sql)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(e)
.bind(s)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(s)
.bind(e)
.bind(e)
.bind(s)
.fetch_optional(executor)
.await?;
Ok(row.and_then(|(v,)| v))
}
pub(crate) async fn count_integrated_ops<'e, E>(executor: E) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as(
"SELECT
(SELECT COUNT(*) FROM ChainOp)
+
(SELECT COUNT(*) FROM WarrantOp)",
)
.fetch_one(executor)
.await?;
Ok(n)
}
const LIMBO_CHAIN_OP_READY_PRED: &str =
"sys_validation_status = 2 OR (sys_validation_status = 1 AND app_validation_status IN (1, 2))";
fn limbo_chain_op_not_ready_pred() -> String {
format!("NOT COALESCE(({LIMBO_CHAIN_OP_READY_PRED}), 0)")
}
pub(crate) async fn limbo_state_counts<'e, E>(executor: E) -> sqlx::Result<(i64, i64, i64)>
where
E: Executor<'e, Database = Sqlite>,
{
let sql = format!(
"SELECT
(
(SELECT COUNT(*) FROM LimboChainOp WHERE {not_ready})
+
(SELECT COUNT(*) FROM LimboWarrantOp WHERE sys_validation_status IS NULL)
) AS validation_limbo,
(
(SELECT COUNT(*) FROM LimboChainOp WHERE {ready})
+
(SELECT COUNT(*) FROM LimboWarrantOp WHERE sys_validation_status IN (1, 2))
) AS integration_limbo,
(
(SELECT COUNT(*) FROM ChainOp WHERE locally_validated = 1)
+
(SELECT COUNT(*) FROM WarrantOp)
) AS integrated",
ready = LIMBO_CHAIN_OP_READY_PRED,
not_ready = limbo_chain_op_not_ready_pred(),
);
let counts: (i64, i64, i64) = sqlx::query_as(sqlx::AssertSqlSafe(sql))
.fetch_one(executor)
.await?;
Ok(counts)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn count_valid_integrated_ops<'e, E>(executor: E) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as(
"SELECT COUNT(*) FROM ChainOp WHERE locally_validated = 1 AND validation_status = 1",
)
.fetch_one(executor)
.await?;
Ok(n)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn count_valid_not_integrated_ops<'e, E>(executor: E) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as(
"SELECT COUNT(*) FROM LimboChainOp \
WHERE sys_validation_status = 1 AND app_validation_status = 1",
)
.fetch_one(executor)
.await?;
Ok(n)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn count_pending_ops_for_author<'e, E>(
executor: E,
author: &AgentPubKey,
) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as(
"SELECT COUNT(*) FROM LimboChainOp \
JOIN Action ON Action.hash = LimboChainOp.action_hash \
WHERE Action.author = ?",
)
.bind(author.get_raw_36())
.fetch_one(executor)
.await?;
Ok(n)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn rejected_integrated_op_hashes<'e, E>(executor: E) -> sqlx::Result<Vec<Vec<u8>>>
where
E: Executor<'e, Database = Sqlite>,
{
let rows: Vec<(Vec<u8>,)> = sqlx::query_as(
"SELECT hash FROM ChainOp \
WHERE locally_validated = 1 AND validation_status = 2 \
ORDER BY hash",
)
.fetch_all(executor)
.await?;
Ok(rows.into_iter().map(|(h,)| h).collect())
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn count_all_ops<'e, E>(executor: E) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as(
"SELECT
(SELECT COUNT(*) FROM ChainOp)
+ (SELECT COUNT(*) FROM LimboChainOp)
+ (SELECT COUNT(*) FROM WarrantOp)
+ (SELECT COUNT(*) FROM LimboWarrantOp)",
)
.fetch_one(executor)
.await?;
Ok(n)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn op_requires_receipt<'e, E>(
executor: E,
op_hash: &DhtOpHash,
) -> sqlx::Result<bool>
where
E: Executor<'e, Database = Sqlite>,
{
let row: Option<(bool,)> = sqlx::query_as("SELECT require_receipt FROM ChainOp WHERE hash = ?")
.bind(op_hash.get_raw_36())
.fetch_optional(executor)
.await?;
Ok(row.map(|(b,)| b).unwrap_or(false))
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn limbo_op_exists<'e, E>(executor: E, op_hash: &DhtOpHash) -> sqlx::Result<bool>
where
E: Executor<'e, Database = Sqlite>,
{
let (b,): (bool,) = sqlx::query_as("SELECT EXISTS(SELECT 1 FROM LimboChainOp WHERE hash = ?)")
.bind(op_hash.get_raw_36())
.fetch_one(executor)
.await?;
Ok(b)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn limbo_op_hashes_requiring_receipt<'e, E>(
executor: E,
) -> sqlx::Result<Vec<Vec<u8>>>
where
E: Executor<'e, Database = Sqlite>,
{
let rows: Vec<(Vec<u8>,)> =
sqlx::query_as("SELECT hash FROM LimboChainOp WHERE require_receipt = 1 ORDER BY hash")
.fetch_all(executor)
.await?;
Ok(rows.into_iter().map(|(h,)| h).collect())
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn get_ops_at_basis<'e, E>(
executor: E,
basis: &AnyLinkableHash,
) -> sqlx::Result<Vec<Vec<u8>>>
where
E: Executor<'e, Database = Sqlite>,
{
let rows: Vec<(Vec<u8>,)> = sqlx::query_as(
"SELECT hash FROM ChainOp WHERE basis_hash = ? AND locally_validated = 1 ORDER BY hash",
)
.bind(basis.get_raw_36())
.fetch_all(executor)
.await?;
Ok(rows.into_iter().map(|(h,)| h).collect())
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn count_entries<'e, E>(executor: E) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as("SELECT COUNT(*) FROM Entry")
.fetch_one(executor)
.await?;
Ok(n)
}