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
AND (ChainOp.op_type != 2 OR Action.private_entry = 0)
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
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.when_integrated >= ?
AND ChainOp.locally_validated = 1
AND (ChainOp.op_type != 2 OR Action.private_entry = 0)
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.op_type != 2 OR Action.private_entry = 0)
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
AND (ChainOp.op_type != 2 OR Action.private_entry = 0)
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)
}
#[cfg(any(test, feature = "inspection"))]
pub(crate) async fn count_private_entries_in_public_table<'e, E>(executor: E) -> sqlx::Result<i64>
where
E: Executor<'e, Database = Sqlite>,
{
let (n,): (i64,) = sqlx::query_as(
"SELECT COUNT(*) FROM Entry \
JOIN Action ON Action.entry_hash = Entry.hash \
WHERE Action.private_entry = 1",
)
.fetch_one(executor)
.await?;
Ok(n)
}
#[cfg(test)]
mod tests {
use crate::handles::DbWrite;
use crate::kind::Dht;
use crate::test_open_db;
use holo_hash::{AgentPubKey, DnaHash};
use sqlx::{Pool, Sqlite};
use std::sync::Arc;
const STORE_ENTRY: i64 = 2;
const STORE_RECORD: i64 = 1;
const AUTHOR: u8 = 7;
fn dht_id() -> Dht {
Dht::new(Arc::new(DnaHash::from_raw_36(vec![0u8; 36])))
}
async fn seed_op(
pool: &Pool<Sqlite>,
op_tag: u8,
op_type: i64,
private_entry: i64,
timestamp: i64,
) -> Vec<u8> {
let op_hash = vec![op_tag; 36];
let action_hash = vec![op_tag + 10; 36];
let basis_hash = vec![op_tag + 20; 36];
sqlx::query(
"INSERT INTO Action
(hash, author, seq, prev_hash, timestamp, action_type,
action_data, signature, entry_hash, private_entry, record_validity)
VALUES (?, ?, 0, NULL, ?, 0, ?, ?, NULL, ?, NULL)",
)
.bind(&action_hash)
.bind(vec![AUTHOR; 36])
.bind(timestamp)
.bind(vec![0u8]) .bind(vec![0u8]) .bind(private_entry)
.execute(pool)
.await
.unwrap();
sqlx::query(
"INSERT INTO ChainOp
(hash, op_type, action_hash, basis_hash, storage_center_loc,
validation_status, locally_validated, require_receipt,
when_received, when_integrated, serialized_size)
VALUES (?, ?, ?, ?, 0, 1, 1, 0, ?, ?, 10)",
)
.bind(&op_hash)
.bind(op_type)
.bind(&action_hash)
.bind(&basis_hash)
.bind(timestamp)
.bind(timestamp)
.execute(pool)
.await
.unwrap();
op_hash
}
async fn seed_filter_fixture() -> (DbWrite<Dht>, Vec<u8>, Vec<u8>, Vec<u8>) {
let db = test_open_db(dht_id()).await.unwrap();
let public = seed_op(db.pool(), 1, STORE_ENTRY, 0, 2000).await;
let private = seed_op(db.pool(), 2, STORE_ENTRY, 1, 1000).await;
let record = seed_op(db.pool(), 3, STORE_RECORD, 1, 3000).await;
(db, public, private, record)
}
#[tokio::test]
async fn time_slice_read_withholds_private_store_entry() {
let (db, public, private, record) = seed_filter_fixture().await;
let hashes: Vec<Vec<u8>> = db
.as_ref()
.op_hashes_in_time_slice(0, u32::MAX, 0, i64::MAX)
.await
.unwrap()
.into_iter()
.map(|r| r.hash)
.collect();
assert!(
hashes.contains(&public),
"public CreateEntry must be served"
);
assert!(
hashes.contains(&record),
"CreateRecord op with a private entry must be served (body withheld)"
);
assert!(
!hashes.contains(&private),
"private CreateEntry op must never be advertised in a time slice"
);
}
#[tokio::test]
async fn ids_since_read_withholds_private_store_entry() {
let (db, public, private, record) = seed_filter_fixture().await;
let hashes: Vec<Vec<u8>> = db
.as_ref()
.op_ids_since_time_batch(0, u32::MAX, 0, 100)
.await
.unwrap()
.into_iter()
.map(|r| r.hash)
.collect();
assert!(
hashes.contains(&public),
"public CreateEntry must be served"
);
assert!(hashes.contains(&record), "CreateRecord op must be served");
assert!(
!hashes.contains(&private),
"private CreateEntry op must never be advertised in the since cursor"
);
}
#[tokio::test]
async fn for_wire_read_withholds_private_store_entry_even_when_requested() {
let (db, public, private, record) = seed_filter_fixture().await;
let requested = vec![public.clone(), private.clone(), record.clone()];
let hashes: Vec<Vec<u8>> = db
.as_ref()
.get_chain_ops_for_wire(&requested)
.await
.unwrap()
.into_iter()
.map(|r| r.op_hash)
.collect();
assert!(
hashes.contains(&public),
"public CreateEntry must be served"
);
assert!(hashes.contains(&record), "CreateRecord op must be served");
assert!(
!hashes.contains(&private),
"private CreateEntry op must never be served by hash"
);
}
#[tokio::test]
async fn earliest_timestamp_ignores_private_store_entry() {
let (db, _public, _private, _record) = seed_filter_fixture().await;
let earliest = db
.as_ref()
.earliest_authored_timestamp_in_arc(0, u32::MAX)
.await
.unwrap();
assert_eq!(
earliest,
Some(2000),
"earliest timestamp must reflect the earliest *servable* op, not a withheld private one"
);
}
#[tokio::test]
async fn ops_to_publish_read_withholds_private_store_entry() {
let (db, public, private, record) = seed_filter_fixture().await;
let author = AgentPubKey::from_raw_36(vec![AUTHOR; 36]);
let hashes: Vec<Vec<u8>> = db
.as_ref()
.ops_to_publish_for_wire(&author)
.await
.unwrap()
.into_iter()
.map(|r| r.op_hash)
.collect();
assert!(
hashes.contains(&public),
"public CreateEntry must be published"
);
assert!(
hashes.contains(&record),
"CreateRecord op must be published"
);
assert!(
!hashes.contains(&private),
"private CreateEntry op must never be published"
);
}
}