commit d8e5f41a3dd6c980dc8c86883478161e4bb9fe09
parent 448f4106f2b8b672ae05a8cc53c8b15b47746774
Author: triesap <tyson@radroots.org>
Date: Tue, 28 Jul 2026 17:53:26 +0000
event_store: bound reconciliation counts
- share SQLite count conversion
- preserve field diagnostics
- accept the signed maximum
- reject the exact one-over value
Diffstat:
2 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/contracts/event_store_production_sources.toml b/contracts/event_store_production_sources.toml
@@ -47,7 +47,7 @@ sha256 = "fbd8a3b36d7f36e7b0d301aee0847d42c3908659f066cafcae3e247d67a75845"
[[sources]]
path = "crates/event_store/src/nip09/reconciliation_v1.rs"
-sha256 = "e532a0e1224896f94efe40aa1e6bc187853d0a919a8427b6a776d32733994e82"
+sha256 = "ddae49cacd4f660da09d0d4c5892d3074d4c24453801026c548f268e2608438c"
[[sources]]
path = "crates/event_store/src/nip09/reconciliation_v1/raw_source_rebuild.rs"
diff --git a/crates/event_store/src/nip09/reconciliation_v1.rs b/crates/event_store/src/nip09/reconciliation_v1.rs
@@ -562,6 +562,19 @@ fn reconciliation_capacity_value(
})
}
+fn sqlite_reconciliation_count(
+ value: u128,
+ reason: &'static str,
+) -> Result<i64, RadrootsEventStoreError> {
+ match i64::try_from(value) {
+ Ok(value) => Ok(value),
+ Err(_) => Err(RadrootsEventStoreError::MigrationHookStateDrift {
+ hook_id: NIP09_HOOK_ID,
+ reason: reason.to_owned(),
+ }),
+ }
+}
+
pub(crate) async fn apply_reconciliation_hook(
connection: &mut SqliteConnection,
generation_provider: &dyn SourceGenerationProvider,
@@ -572,18 +585,14 @@ pub(crate) async fn apply_reconciliation_hook(
validate_projection_cursor_authority(connection).await?;
let snapshot = load_reconciliation_snapshot(connection, limits).await?;
let events = snapshot.events;
- let raw_event_count = i64::try_from(snapshot.capacity.raw_events).map_err(|_| {
- RadrootsEventStoreError::MigrationHookStateDrift {
- hook_id: NIP09_HOOK_ID,
- reason: "raw event count exceeds SQLite integer range".to_owned(),
- }
- })?;
- let raw_tag_count = i64::try_from(snapshot.capacity.raw_tags).map_err(|_| {
- RadrootsEventStoreError::MigrationHookStateDrift {
- hook_id: NIP09_HOOK_ID,
- reason: "raw tag count exceeds SQLite integer range".to_owned(),
- }
- })?;
+ let raw_event_count = sqlite_reconciliation_count(
+ u128::from(snapshot.capacity.raw_events),
+ "raw event count exceeds SQLite integer range",
+ )?;
+ let raw_tag_count = sqlite_reconciliation_count(
+ u128::from(snapshot.capacity.raw_tags),
+ "raw tag count exceeds SQLite integer range",
+ )?;
let raw_high_water_seq = events.last().map(|event| event.seq).unwrap_or(0);
let source_state_count: i64 =
sqlx::query_scalar("SELECT COUNT(*) FROM radroots_event_store_source_state")
@@ -1236,12 +1245,10 @@ pub(crate) async fn synchronize_after_insert(
.bind(inserted_event_id)
.fetch_one(&mut *connection)
.await?;
- let inserted_tag_count = i64::try_from(inserted_tag_count).map_err(|_| {
- RadrootsEventStoreError::MigrationHookStateDrift {
- hook_id: NIP09_HOOK_ID,
- reason: "inserted tag count exceeds SQLite integer range".to_owned(),
- }
- })?;
+ let inserted_tag_count = sqlite_reconciliation_count(
+ inserted_tag_count as u128,
+ "inserted tag count exceeds SQLite integer range",
+ )?;
if actual_inserted_tag_count != inserted_tag_count
|| inserted_seq <= prior.raw_high_water_seq
|| actual_high_water != inserted_seq
@@ -3824,6 +3831,18 @@ mod tests {
),
Err(RadrootsEventStoreError::MigrationHookStateDrift { .. })
));
+ assert_eq!(
+ sqlite_reconciliation_count(i64::MAX as u128, "fixture count range")
+ .expect("maximum SQLite count"),
+ i64::MAX,
+ );
+ assert!(matches!(
+ sqlite_reconciliation_count(i64::MAX as u128 + 1, "fixture count range"),
+ Err(RadrootsEventStoreError::MigrationHookStateDrift {
+ hook_id: NIP09_HOOK_ID,
+ reason,
+ }) if reason == "fixture count range"
+ ));
assert!(matches!(
EventAdmission::from_registry_v7(RadrootsRegistryV7AdmissionDecision::Defect {
code: "fixture_defect",