lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit d9d8a671803bcab2ff8e13c1556b68528db87d76
parent 8c54865ac4eee33f584d3cd821535435ca6f9eed
Author: triesap <tyson@radroots.org>
Date:   Tue, 28 Jul 2026 23:38:45 +0000

event: use identity-owned public keys

- remove the event-owned PublicKey alias and import the canonical identity type directly
- store canonical identity keys in event references and encode hex only at wire boundaries
- supersede the immutable NIP-09 executor with an evidence-bound explicit-encoder successor
- migrate codec fixtures and downstream consumers while preserving conformance coverage

Diffstat:
MCargo.lock | 1+
Mcrates/event/src/calendar.rs | 3++-
Mcrates/event/src/comment.rs | 5+++--
Mcrates/event/src/draft.rs | 5++---
Mcrates/event/src/envelope.rs | 5++---
Mcrates/event/src/event_head/v1.rs | 3++-
Mcrates/event/src/ids.rs | 2+-
Mcrates/event/src/knowledge.rs | 4++--
Mcrates/event/src/lib.rs | 3++-
Mcrates/event/src/order.rs | 5++---
Mcrates/event/src/reply.rs | 3++-
Mcrates/event/src/trade.rs | 6+++---
Mcrates/event/tests/package_boundary.rs | 18++++++++++++++++++
Mcrates/event_codec/src/event_ref.rs | 22+++++++++++-----------
Mcrates/event_codec/src/knowledge/encode.rs | 14+++++++++-----
Mcrates/event_codec/tests/common/mod.rs | 5+++--
Mcrates/event_codec/tests/event_ref.rs | 49++++++++++++++++++++++++++++++++-----------------
Mcrates/event_codec/tests/knowledge.rs | 5++++-
Mcrates/event_store/contracts/source_maintenance_v1.manifest.json | 55+++++++++++++++++++++++++++++++------------------------
Mcrates/event_store/contracts/source_maintenance_v1.manifest.sha256 | 2+-
Mcrates/event_store/src/generated/source_maintenance_manifest.rs | 6+++---
Mcrates/event_store/src/nip09/reconciliation_v1.rs | 20++++++++++++++++++++
Mcrates/event_store/src/store/food_availability_projection_v1.rs | 2+-
Acrates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs | 513+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/test_fixtures/Cargo.toml | 1+
Mcrates/test_fixtures/src/knowledge.rs | 6+++++-
Mtools/xtask/src/contract/food_availability_projection.rs | 24++++++++++++++++++++++++
Mtools/xtask/src/contract/nip09_reconciliation.rs | 8++++----
Mtools/xtask/src/contract/source_maintenance.rs | 55++++++++++++++++++++++++++++++++++++++++++++++++++++++-
29 files changed, 758 insertions(+), 92 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -5098,6 +5098,7 @@ name = "radroots_test_fixtures" version = "0.1.0-alpha" dependencies = [ "radroots_event", + "radroots_identity", ] [[package]] diff --git a/crates/event/src/calendar.rs b/crates/event/src/calendar.rs @@ -8,7 +8,7 @@ use alloc::{ use core::{fmt, ops::RangeInclusive, str::FromStr}; use crate::ids::{ - PublicKey, RadrootsAddressableCoordinate, RadrootsAddressableCoordinateParts, RadrootsDTag, + RadrootsAddressableCoordinate, RadrootsAddressableCoordinateParts, RadrootsDTag, RadrootsEventId, RadrootsRelayUrl, parse_public_key, }; use crate::media::RadrootsAuthoredImage; @@ -17,6 +17,7 @@ use crate::wire::v1::{ DEFAULT_TAG_TOTAL_MAX_BYTES, }; use radroots_blossom::BlobUrl; +use radroots_identity::PublicKey; use url_nostd::Url; pub const RADROOTS_CALENDAR_SECONDS_PER_DAY: u64 = 86_400; diff --git a/crates/event/src/comment.rs b/crates/event/src/comment.rs @@ -7,8 +7,8 @@ use core::fmt; use crate::{ ids::{ - PublicKey, RadrootsAddressableCoordinate, RadrootsAddressableCoordinateParts, - RadrootsEventId, RadrootsIdParseError, parse_public_key, + RadrootsAddressableCoordinate, RadrootsAddressableCoordinateParts, RadrootsEventId, + RadrootsIdParseError, parse_public_key, }, kinds::{ KIND_CALENDAR_DATE_EVENT, KIND_CALENDAR_TIME_EVENT, KIND_CLASSIFIED_LISTING, KIND_COMMENT, @@ -19,6 +19,7 @@ use crate::{ DEFAULT_TAG_MAX_COUNT, DEFAULT_TAG_TOTAL_ELEMENT_MAX_COUNT, DEFAULT_TAG_TOTAL_MAX_BYTES, }, }; +use radroots_identity::PublicKey; pub const RADROOTS_NIP22_COMMENT_CONTENT_MAX_BYTES: usize = DEFAULT_CONTENT_MAX_BYTES; pub const RADROOTS_NIP22_COMMENT_TAG_MAX_COUNT: usize = DEFAULT_TAG_MAX_COUNT; diff --git a/crates/event/src/draft.rs b/crates/event/src/draft.rs @@ -11,9 +11,7 @@ use crate::contract::registry_v7::{ RadrootsEventAuthoringPolicy, RadrootsEventContract, event_contract, validate_event_contract_parts, }; -use crate::ids::{ - PublicKey, RadrootsEventId, RadrootsEventSignature, RadrootsIdParseError, parse_public_key, -}; +use crate::ids::{RadrootsEventId, RadrootsEventSignature, RadrootsIdParseError, parse_public_key}; use crate::wire::v1::{ RadrootsCanonicalEventIdError, RadrootsEventWireError, RadrootsNip01EventWire, canonical_nip01_event_id_preimage, compute_canonical_nip01_event_id, @@ -23,6 +21,7 @@ use crate::{ RadrootsEventTimestamp, }; use core::fmt; +use radroots_identity::PublicKey; #[derive(Clone, Debug, PartialEq, Eq)] pub enum RadrootsDraftError { diff --git a/crates/event/src/envelope.rs b/crates/event/src/envelope.rs @@ -6,14 +6,13 @@ use alloc::{string::String, vec::Vec}; #[cfg(any(feature = "std", test))] use std::{string::String, vec::Vec}; -use crate::ids::{ - PublicKey, RadrootsEventId, RadrootsEventSignature, RadrootsIdParseError, parse_public_key, -}; +use crate::ids::{RadrootsEventId, RadrootsEventSignature, RadrootsIdParseError, parse_public_key}; use crate::wire::v1::{ DEFAULT_CONTENT_MAX_BYTES, DEFAULT_TAG_ELEMENT_MAX_BYTES, DEFAULT_TAG_MAX_COUNT, DEFAULT_TAG_TOTAL_ELEMENT_MAX_COUNT, DEFAULT_TAG_TOTAL_MAX_BYTES, RadrootsNip01EventWire, }; use core::fmt; +use radroots_identity::PublicKey; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RadrootsEventTimestamp(u64); diff --git a/crates/event/src/event_head/v1.rs b/crates/event/src/event_head/v1.rs @@ -9,8 +9,9 @@ use crate::contract::registry_v7::{ RadrootsContractMatchError, RadrootsEventClass, RadrootsEventContract, identify_event_contract, }; use crate::envelope::{RadrootsEventEnvelope, RadrootsEventKindClass, RadrootsEventTag}; -use crate::ids::{PublicKey, RadrootsDTag, RadrootsEventId, RadrootsIdParseError}; +use crate::ids::{RadrootsDTag, RadrootsEventId, RadrootsIdParseError}; use crate::tags::TAG_D; +use radroots_identity::PublicKey; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum RadrootsEventHeadCoordinate { diff --git a/crates/event/src/ids.rs b/crates/event/src/ids.rs @@ -10,7 +10,7 @@ use core::{ fmt::{self, Write as _}, str::FromStr, }; -pub(crate) use radroots_identity::PublicKey; +use radroots_identity::PublicKey; use url_nostd::Url; use crate::kinds::KIND_CLASSIFIED_LISTING; diff --git a/crates/event/src/knowledge.rs b/crates/event/src/knowledge.rs @@ -207,7 +207,6 @@ fn validate_event_ref( field: &'static str, ) -> Result<(), RadrootsKnowledgeValidationError> { validate_event_id(&event_ref.id, field)?; - validate_pubkey(&event_ref.author, field)?; if let Some(d_tag) = event_ref.d_tag.as_deref() { RadrootsDTag::parse(d_tag) .map(|_| ()) @@ -925,7 +924,8 @@ mod tests { fn event_ref_with_kind(kind: u32) -> RadrootsEventRef { RadrootsEventRef { id: "0".repeat(64), - author: crate::test_valid_hex_64('1'), + author: radroots_identity::PublicKey::from_hex(&crate::test_valid_hex_64('1')) + .expect("fixture public key"), kind, d_tag: None, relays: None, diff --git a/crates/event/src/lib.rs b/crates/event/src/lib.rs @@ -122,7 +122,8 @@ pub use wire::{ #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsEventRef { pub id: String, - pub author: String, + #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] + pub author: radroots_identity::PublicKey, pub kind: u32, pub d_tag: Option<String>, pub relays: Option<Vec<String>>, diff --git a/crates/event/src/order.rs b/crates/event/src/order.rs @@ -8,9 +8,7 @@ use alloc::{ #[cfg(test)] use crate::ids::RadrootsOrderQuoteId; -use crate::ids::{ - PublicKey, RadrootsClassifiedListingAddress, RadrootsInventoryBinId, RadrootsOrderId, -}; +use crate::ids::{RadrootsClassifiedListingAddress, RadrootsInventoryBinId, RadrootsOrderId}; use crate::kinds::*; #[cfg(test)] use crate::operational_listing::RadrootsOperationalListingParseError; @@ -18,6 +16,7 @@ pub use crate::order_economics::*; #[cfg(test)] use crate::trade_validation::RadrootsOperationalListingValidationError; use radroots_core::{Currency, Decimal, Money}; +use radroots_identity::PublicKey; pub const RADROOTS_COMMERCIAL_LISTING_DOMAIN: &str = "trade:listing"; pub const RADROOTS_ORDER_ENVELOPE_VERSION: u16 = 1; diff --git a/crates/event/src/reply.rs b/crates/event/src/reply.rs @@ -3,13 +3,14 @@ use alloc::string::String; use core::fmt; use crate::{ - ids::{PublicKey, RadrootsEventId, RadrootsIdParseError, parse_public_key}, + ids::{RadrootsEventId, RadrootsIdParseError, parse_public_key}, post::{ RADROOTS_POST_CONTENT_MAX_BYTES, RADROOTS_POST_EVENT_WIRE_MAX_BYTES, RADROOTS_POST_TAG_ELEMENT_MAX_BYTES, RADROOTS_POST_TAG_TOTAL_MAX_BYTES, }, relay_hint::RadrootsNostrRelayHint, }; +use radroots_identity::PublicKey; const RADROOTS_NIP10_REPLY_SIGNED_EVENT_FIXED_MAX_BYTES: usize = "{\"id\":\"".len() + 64 diff --git a/crates/event/src/trade.rs b/crates/event/src/trade.rs @@ -12,14 +12,14 @@ use std::{string::String, vec::Vec}; use core::fmt; use crate::ids::{ - PublicKey, RadrootsClassifiedListingAddress, RadrootsDTag, RadrootsEventId, - RadrootsIdParseError, RadrootsInventoryBinId, RadrootsTradeCandidateId, RadrootsTradeId, - RadrootsTradeMutationId, + RadrootsClassifiedListingAddress, RadrootsDTag, RadrootsEventId, RadrootsIdParseError, + RadrootsInventoryBinId, RadrootsTradeCandidateId, RadrootsTradeId, RadrootsTradeMutationId, }; use crate::kinds::{ KIND_TRADE_CANCELLATION, KIND_TRADE_DECISION, KIND_TRADE_PROPOSAL, KIND_TRADE_REVISION_DECISION, KIND_TRADE_REVISION_PROPOSAL, }; +use radroots_identity::PublicKey; #[cfg(feature = "serde")] use serde::{ Deserialize, Deserializer, Serialize, diff --git a/crates/event/tests/package_boundary.rs b/crates/event/tests/package_boundary.rs @@ -10,6 +10,7 @@ use radroots_event::{ id as _, listing as _, media as _, post as _, profile as _, social as _, tag as _, trade as _, wire as _, }; +use radroots_identity::PublicKey; const MANIFEST: &str = include_str!("../Cargo.toml"); const ROOT: &str = include_str!("../src/lib.rs"); @@ -90,6 +91,23 @@ fn canonical_identifier_api_owns_bytes_and_requires_explicit_text_encoding() { assert!(!RELAY_HINT.contains("impl Borrow<str>")); } +#[test] +fn event_references_use_the_identity_owned_public_key() { + let author = + PublicKey::from_hex("585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df") + .expect("valid public key"); + let reference = radroots_event::RadrootsEventRef { + id: "a".repeat(64), + author, + kind: 1, + d_tag: None, + relays: None, + }; + + assert_eq!(reference.author, author); + assert!(!IDENTIFIERS.contains("pub(crate) use radroots_identity::PublicKey")); +} + fn table_keys<'a>(manifest: &'a str, heading: &str) -> BTreeSet<&'a str> { let Some((_, table)) = manifest.split_once(heading) else { return BTreeSet::new(); diff --git a/crates/event_codec/src/event_ref.rs b/crates/event_codec/src/event_ref.rs @@ -18,7 +18,7 @@ pub fn build_event_ref_tag(tag: &str, event: &RadrootsEventRef) -> Vec<String> { let mut out = Vec::with_capacity(5 + relays_len); out.push(tag.to_string()); out.push(event.id.clone()); - out.push(event.author.clone()); + out.push(event.author.to_hex()); out.push(event.kind.to_string()); out.push(event.d_tag.clone().unwrap_or_default()); if let Some(relays) = &event.relays { @@ -40,7 +40,7 @@ pub fn parse_event_ref_tag( let id = &tag[1]; RadrootsEventId::parse(id).map_err(|_| EventParseError::InvalidTag(tag_name))?; let author = &tag[2]; - PublicKey::from_hex(author).map_err(|_| EventParseError::InvalidTag(tag_name))?; + let author = PublicKey::from_hex(author).map_err(|_| EventParseError::InvalidTag(tag_name))?; let kind_s = &tag[3]; let kind: u32 = kind_s .parse() @@ -68,7 +68,7 @@ pub fn parse_event_ref_tag( Ok(RadrootsEventRef { id: id.clone(), - author: author.clone(), + author, kind, d_tag, relays, @@ -102,17 +102,18 @@ pub fn push_nip10_ref_tags( } tags.push(e_tag); - let p_tag = vec![tag_p.to_string(), event.author.clone()]; + let author = event.author.to_hex(); + let p_tag = vec![tag_p.to_string(), author.clone()]; tags.push(p_tag); let k_tag = vec![tag_k.to_string(), kind_str.clone()]; tags.push(k_tag); if let Some(d_tag) = event.d_tag.as_deref().filter(|v| !v.is_empty()) { - let mut addr = String::with_capacity(kind_str.len() + event.author.len() + d_tag.len() + 2); + let mut addr = String::with_capacity(kind_str.len() + author.len() + d_tag.len() + 2); addr.push_str(&kind_str); addr.push(':'); - addr.push_str(&event.author); + addr.push_str(&author); addr.push(':'); addr.push_str(d_tag); @@ -146,9 +147,8 @@ pub fn parse_nip10_ref_tags( let p_tag = find_event_ref_tag(tags, tag_p).ok_or(EventParseError::MissingTag(tag_p))?; let author = p_tag.get(1).ok_or(EventParseError::InvalidTag(tag_p))?; - if author.trim().is_empty() { - return Err(EventParseError::InvalidTag(tag_p)); - } + let author = PublicKey::from_hex(author).map_err(|_| EventParseError::InvalidTag(tag_p))?; + let author_hex = author.to_hex(); let k_tag = find_event_ref_tag(tags, tag_k).ok_or(EventParseError::MissingTag(tag_k))?; let kind_key = k_tag.get(1).ok_or(EventParseError::InvalidTag(tag_k))?; @@ -170,7 +170,7 @@ pub fn parse_nip10_ref_tags( let kind_part = parts.next(); let author_part = parts.next(); let d_part = parts.next(); - if kind_part != Some(kind_key.as_str()) || author_part != Some(author.as_str()) { + if kind_part != Some(kind_key.as_str()) || author_part != Some(author_hex.as_str()) { continue; } if let Some(d) = d_part @@ -188,7 +188,7 @@ pub fn parse_nip10_ref_tags( Ok(RadrootsEventRef { id: id.clone(), - author: author.clone(), + author, kind, d_tag, relays, diff --git a/crates/event_codec/src/knowledge/encode.rs b/crates/event_codec/src/knowledge/encode.rs @@ -35,6 +35,7 @@ use crate::error::EventEncodeError; use crate::event_ref::build_event_ref_tag; use crate::wire::empty_content; use radroots_event::wire::RadrootsNip01EventWireParts; +use radroots_identity::PublicKey; const TAG_TITLE: &str = "title"; const TAG_SOURCE: &str = "source"; @@ -119,10 +120,13 @@ fn push_wiki_version_ref_tags( tags.push(marker_event_tag(version_ref, marker)); } -fn review_target_ref(target: &RadrootsKnowledgeReviewTarget) -> RadrootsEventRef { - RadrootsEventRef { +fn review_target_ref( + target: &RadrootsKnowledgeReviewTarget, +) -> Result<RadrootsEventRef, EventEncodeError> { + Ok(RadrootsEventRef { id: target.event_id.clone(), - author: target.author_pubkey.clone(), + author: PublicKey::from_hex(&target.author_pubkey) + .map_err(|_| EventEncodeError::InvalidField("review_target"))?, kind: target.kind, d_tag: target .address @@ -133,7 +137,7 @@ fn review_target_ref(target: &RadrootsKnowledgeReviewTarget) -> RadrootsEventRef } else { Some(target.relays.clone()) }, - } + }) } fn json_content<T: Serialize>(value: &T) -> Result<String, EventEncodeError> { @@ -331,7 +335,7 @@ pub fn knowledge_review_build_tags( let mut tags = custom_tags(RADROOTS_KNOWLEDGE_REVIEW_SCHEMA); tags.push(build_event_ref_tag( TAG_REVIEW_TARGET, - &review_target_ref(&review.target), + &review_target_ref(&review.target)?, )); push_event_refs(&mut tags, TAG_EVIDENCE, &review.evidence_refs); Ok(tags) diff --git a/crates/event_codec/tests/common/mod.rs b/crates/event_codec/tests/common/mod.rs @@ -3,6 +3,7 @@ use radroots_event::{ RadrootsEventEnvelope, RadrootsEventEnvelopeParts, RadrootsEventPtr, RadrootsEventRef, }; +use radroots_identity::PublicKey; pub const EVENT_ID: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; pub const AUTHOR: &str = "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"; @@ -14,7 +15,7 @@ pub const EVENT_SIG: &str = concat!( pub fn event_ref(id: &str, author: &str, kind: u32) -> RadrootsEventRef { RadrootsEventRef { id: id.to_string(), - author: author.to_string(), + author: PublicKey::from_hex(author).expect("valid fixture public key"), kind, d_tag: None, relays: None, @@ -30,7 +31,7 @@ pub fn event_ref_with_d( ) -> RadrootsEventRef { RadrootsEventRef { id: id.to_string(), - author: author.to_string(), + author: PublicKey::from_hex(author).expect("valid fixture public key"), kind, d_tag: Some(d_tag.to_string()), relays, diff --git a/crates/event_codec/tests/event_ref.rs b/crates/event_codec/tests/event_ref.rs @@ -162,7 +162,7 @@ fn parse_event_ref_tag_rejects_missing_required_values() { #[test] fn find_event_ref_tag_locates_first_match() { let id = hex_64('b'); - let event = common::event_ref(&id, &hex_64('c'), KIND_POST); + let event = common::event_ref(&id, common::AUTHOR, KIND_POST); let tags = vec![ vec!["p".to_string(), "pubkey".to_string()], build_event_ref_tag("e", &event), @@ -177,7 +177,7 @@ fn find_event_ref_tag_locates_first_match() { fn push_and_parse_nip10_ref_tags_roundtrip_with_and_without_a_tag() { let event = common::event_ref_with_d( "id", - "author", + common::AUTHOR, KIND_POST, "AAAAAAAAAAAAAAAAAAAAAA", Some(vec![RELAY_PRIMARY_WSS.to_string()]), @@ -191,7 +191,7 @@ fn push_and_parse_nip10_ref_tags_roundtrip_with_and_without_a_tag() { assert_eq!(parsed.d_tag, event.d_tag); assert_eq!(parsed.relays, event.relays); - let event = common::event_ref("id2", "author2", KIND_POST); + let event = common::event_ref("id2", common::AUTHOR, KIND_POST); let mut tags = Vec::new(); push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a"); let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); @@ -200,8 +200,13 @@ fn push_and_parse_nip10_ref_tags_roundtrip_with_and_without_a_tag() { assert_eq!(parsed.kind, event.kind); assert!(parsed.d_tag.is_none()); - let event = - common::event_ref_with_d("id3", "author3", KIND_POST, "AAAAAAAAAAAAAAAAAAAAAA", None); + let event = common::event_ref_with_d( + "id3", + common::AUTHOR, + KIND_POST, + "AAAAAAAAAAAAAAAAAAAAAA", + None, + ); let mut tags = Vec::new(); push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a"); let a_tag = tags @@ -237,7 +242,7 @@ fn parse_nip10_ref_tags_rejects_missing_or_invalid_required_tags() { let tags = vec![ vec!["e".to_string(), "id".to_string()], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], vec!["k".to_string(), "bad-kind".to_string()], ]; let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); @@ -264,7 +269,7 @@ fn parse_nip10_ref_tags_rejects_missing_required_values() { let tags = vec![ vec!["e".to_string(), "id".to_string()], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], vec!["k".to_string()], ]; let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); @@ -282,7 +287,7 @@ fn parse_nip10_ref_tags_rejects_missing_p_and_k_tags() { let missing_k = vec![ vec!["e".to_string(), "id".to_string()], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], ]; let err = parse_nip10_ref_tags(&missing_k, "e", "p", "k", "a").unwrap_err(); assert!(matches!(err, EventParseError::MissingTag("k"))); @@ -292,11 +297,16 @@ fn parse_nip10_ref_tags_rejects_missing_p_and_k_tags() { fn parse_nip10_ref_tags_prefers_e_relays_and_can_fall_back_to_a_relays() { let tags = vec![ vec!["e".to_string(), "id".to_string()], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], vec!["k".to_string(), KIND_POST.to_string()], vec![ "a".to_string(), - format!("{}:{}:{}", KIND_POST, "author", "AAAAAAAAAAAAAAAAAAAAAA"), + format!( + "{}:{}:{}", + KIND_POST, + common::AUTHOR, + "AAAAAAAAAAAAAAAAAAAAAA" + ), RELAY_SECONDARY_WSS.to_string(), ], ]; @@ -310,11 +320,16 @@ fn parse_nip10_ref_tags_prefers_e_relays_and_can_fall_back_to_a_relays() { "id".to_string(), RELAY_PRIMARY_WSS.to_string(), ], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], vec!["k".to_string(), KIND_POST.to_string()], vec![ "a".to_string(), - format!("{}:{}:{}", KIND_POST, "author", "AAAAAAAAAAAAAAAAAAAAAA"), + format!( + "{}:{}:{}", + KIND_POST, + common::AUTHOR, + "AAAAAAAAAAAAAAAAAAAAAA" + ), RELAY_SECONDARY_WSS.to_string(), ], ]; @@ -326,7 +341,7 @@ fn parse_nip10_ref_tags_prefers_e_relays_and_can_fall_back_to_a_relays() { fn parse_nip10_ref_tags_skips_invalid_a_tags_until_match() { let tags = vec![ vec!["e".to_string(), "id".to_string()], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], vec!["k".to_string(), KIND_POST.to_string()], vec!["a".to_string()], vec![ @@ -334,7 +349,7 @@ fn parse_nip10_ref_tags_skips_invalid_a_tags_until_match() { format!( "{}:{}:{}", KIND_POST + 1, - "author", + common::AUTHOR, "AAAAAAAAAAAAAAAAAAAAAA" ), RELAY_PRIMARY_WSS.to_string(), @@ -349,7 +364,7 @@ fn parse_nip10_ref_tags_skips_invalid_a_tags_until_match() { ], vec![ "a".to_string(), - format!("{}:{}:", KIND_POST, "author"), + format!("{}:{}:", KIND_POST, common::AUTHOR), RELAY_TERTIARY_WSS.to_string(), ], ]; @@ -360,9 +375,9 @@ fn parse_nip10_ref_tags_skips_invalid_a_tags_until_match() { let tags = vec![ vec!["e".to_string(), "id".to_string()], - vec!["p".to_string(), "author".to_string()], + vec!["p".to_string(), common::AUTHOR.to_string()], vec!["k".to_string(), KIND_POST.to_string()], - vec!["a".to_string(), format!("{}:{}", KIND_POST, "author")], + vec!["a".to_string(), format!("{}:{}", KIND_POST, common::AUTHOR)], ]; let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); assert!(parsed.d_tag.is_none()); diff --git a/crates/event_codec/tests/knowledge.rs b/crates/event_codec/tests/knowledge.rs @@ -44,7 +44,10 @@ fn hex_64(character: char) -> String { fn event_ref(character: char, kind: u32) -> radroots_event::RadrootsEventRef { radroots_event::RadrootsEventRef { id: hex_64(character), - author: hex_64('a'), + author: radroots_identity::PublicKey::from_hex( + "585591529da0bab31b3b1f986611cf5f435dca84f978c89ee8a40cca7103df", + ) + .expect("fixture public key"), kind, d_tag: None, relays: Some(vec!["wss://relay.radroots.example".to_string()]), diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.json b/crates/event_store/contracts/source_maintenance_v1.manifest.json @@ -271,8 +271,8 @@ { "role": "event_envelope_authority", "path": "crates/event/src/envelope.rs", - "byte_length": 31127, - "sha256": "df22443b825e9ad3da1640029ca6ba9c2ef18a36e14d713bb167766be8ffb439", + "byte_length": 31143, + "sha256": "2478ca91b4dedaf8b8aefe653971b70c56c4ee076dcbfdf7f59f97f86c03b748", "hash_algorithm": "sha256_bytes_v1" }, { @@ -285,22 +285,22 @@ { "role": "event_head_v1_authority", "path": "crates/event/src/event_head/v1.rs", - "byte_length": 7515, - "sha256": "ff3236d2d1628aad5674e0d9a3d607b947190a1cf0020143a139cc28413ad36e", + "byte_length": 7538, + "sha256": "fbc738b403afd3a186ab183ff5f457125a7fbfab01a51fd4b46c92a2543a0126", "hash_algorithm": "sha256_bytes_v1" }, { "role": "event_ids_authority", "path": "crates/event/src/ids.rs", - "byte_length": 51018, - "sha256": "c420320528bac5a8381382cf33d5bed87037a2c753c0495bbc1c74a49de2756d", + "byte_length": 51007, + "sha256": "7d5d52dcbdd2ec31d914999960fa58aa279a8d4f8bd380fb6050b9a1019ad2d8", "hash_algorithm": "sha256_bytes_v1" }, { "role": "event_trade_authority", "path": "crates/event/src/trade.rs", - "byte_length": 68354, - "sha256": "a26ad42c55c82ae6c646ac2678819cff26bd8325e53cd56018819bfd14269e33", + "byte_length": 68373, + "sha256": "f72ea517bb2a634420ee7528e0fbd38e524c8a4c09e7be47ffc237d839d9b2ec", "hash_algorithm": "sha256_bytes_v1" }, { @@ -320,15 +320,15 @@ { "role": "event_draft_authority", "path": "crates/event/src/draft.rs", - "byte_length": 58691, - "sha256": "b3b816bd195eb3269bf696c334d8cd51b406ea305f86cd496fe81fd5e8bcd513", + "byte_length": 58707, + "sha256": "c21e1b2e9cf6bfb4e007a32772ba59452deef0c9ba5f652992159e85fe7ccb24", "hash_algorithm": "sha256_bytes_v1" }, { "role": "event_calendar_authority", "path": "crates/event/src/calendar.rs", - "byte_length": 103814, - "sha256": "f7320e27daa113bff6fcc5da837bcce7d3b67c4df5fb9d9434e96f6ada753d6a", + "byte_length": 103837, + "sha256": "928e5a3df61ab08565cecf20a5045352236bc3d807127f1cd66294d60ee65621", "hash_algorithm": "sha256_bytes_v1" }, { @@ -355,8 +355,8 @@ { "role": "event_comment_authority", "path": "crates/event/src/comment.rs", - "byte_length": 36411, - "sha256": "9f3e1b6457945a2defb1a1cf15d2b0230c1a0d2b72861fb5881859cd936299c7", + "byte_length": 36434, + "sha256": "ec944bd868deb71215c915a35e79ff26715317fdb62c0fbbb5f1d0993f769c38", "hash_algorithm": "sha256_bytes_v1" }, { @@ -593,8 +593,15 @@ { "role": "source_generation_rebuild_authority", "path": "crates/event_store/src/nip09/reconciliation_v1.rs", - "byte_length": 184499, - "sha256": "883f7695f71e76b28e2dbd428d0ddb821ff62ae319cde61f823c62203869742b", + "byte_length": 185215, + "sha256": "575c9fb25b1f728144f5fe649007be705092d9b137f51fd8d771439871bfa09a", + "hash_algorithm": "sha256_bytes_v1" + }, + { + "role": "nip09_successor_result_vector_executor", + "path": "crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs", + "byte_length": 17983, + "sha256": "a1d919aa545d7c506d7ae8740445025732c2cda302790d243a4ff3d6cee898f6", "hash_algorithm": "sha256_bytes_v1" }, { @@ -628,8 +635,8 @@ { "role": "food_projection_store", "path": "crates/event_store/src/store/food_availability_projection_v1.rs", - "byte_length": 48970, - "sha256": "9e0e63d6ca74e49d8ec77f088927983aa4d89a5dae4c9f2d766159e70ed53534", + "byte_length": 48959, + "sha256": "a01ed95983481a8f0efdcc985635efba122fdbfafc226b7e0b30325af6b4de80", "hash_algorithm": "sha256_bytes_v1" }, { @@ -684,22 +691,22 @@ { "role": "predecessor_successor_governance", "path": "tools/xtask/src/contract/food_availability_projection.rs", - "byte_length": 197115, - "sha256": "a9f6553e26c10e255bbfda158989ecead2698e3e791ce577e290ffa88231692b", + "byte_length": 198638, + "sha256": "66a703e5284d0ce91a95d519eee93bd9763d4ab24b7fe8ef39672064118ed391", "hash_algorithm": "sha256_bytes_v1" }, { "role": "transitive_predecessor_membership_governance", "path": "tools/xtask/src/contract/nip09_reconciliation.rs", - "byte_length": 836171, - "sha256": "a4f34441a90334f797d3542e4b0e009d0cfe0b23a1835e9984bd788fda4f493a", + "byte_length": 836177, + "sha256": "7b1d13ca72fbe92dfa6c80d589528aca044bbfd6a960f21a187ccc1090a3cdc1", "hash_algorithm": "sha256_bytes_v1" }, { "role": "source_maintenance_governance", "path": "tools/xtask/src/contract/source_maintenance.rs", - "byte_length": 191437, - "sha256": "bd4682f287b777b2ae1348358511a524a4d2294a16e526553c08f220ca37a001", + "byte_length": 193738, + "sha256": "c24c5a50b2467ecb4a260e29737e3cdf5fffe68028635da3d6ca486563a335fa", "hash_algorithm": "sha256_bytes_v1" }, { diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.sha256 b/crates/event_store/contracts/source_maintenance_v1.manifest.sha256 @@ -1 +1 @@ -267a332c0cfdc9dbc064c98dab23a41ea32ed7d2c2fe31b50777b1c92dbfba01 +368788cfd6e10a1fc7fb9c76c653b408f90ed0a8bb474606b139615c92a1cf43 diff --git a/crates/event_store/src/generated/source_maintenance_manifest.rs b/crates/event_store/src/generated/source_maintenance_manifest.rs @@ -1,8 +1,8 @@ // @generated by `cargo xtask contract source-maintenance-manifest --write`; do not edit. -pub(crate) const SOURCE_MAINTENANCE_MANIFEST_JSON: &str = "{\n \"schema_version\": 1,\n \"contract_id\": \"radroots_event_store.source_maintenance_v1\",\n \"hook_id\": \"source_maintenance_v1\",\n \"manifest_schema\": {\n \"path\": \"crates/event_store/contracts/source_maintenance_v1.manifest.schema.json\",\n \"byte_length\": 12315,\n \"sha256\": \"96ea98a5abf3348b61565954cd34f2c622411d8dd34205ef04134bbe0e3d3f2b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"predecessor\": {\n \"hook_id\": \"food_availability_projection_v1\",\n \"manifest\": {\n \"path\": \"crates/event_store/contracts/food_availability_projection_v1.manifest.json\",\n \"byte_length\": 17455,\n \"sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n },\n \"migration\": {\n \"version\": 4,\n \"name\": \"source_maintenance\",\n \"up\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.up.sql\",\n \"byte_length\": 19841,\n \"sha256\": \"ab2724188f8d08c897eebea2533a635e7c74282a25e84e4c0c37e78b08837a43\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"down\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.down.sql\",\n \"byte_length\": 5172,\n \"sha256\": \"fe44fd53c51545c08ea479b385e6781079dab70fc63da2a3c205d727a00ce860\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"schema_sha256\": \"074f85b663444ac150239ecd8441ea4a96ad83a798a55e22d2e5e2f7ee943a8c\",\n \"catalog\": {\n \"objects\": [\n \"radroots_event_store_source_capacity_delete_guard\",\n \"radroots_event_store_source_capacity_insert_guard\",\n \"radroots_event_store_source_capacity_marker_close_guard\",\n \"radroots_event_store_source_capacity_update_guard\",\n \"radroots_event_store_source_capacity_v1\",\n \"radroots_event_store_source_generation_capacity_advance\",\n \"radroots_event_store_source_generation_capacity_guard\"\n ],\n \"replaced_objects\": [\n \"radroots_event_store_food_availability_image_delete_guard\",\n \"radroots_event_store_food_availability_projection_delete_guard\",\n \"radroots_event_store_source_rebuild_marker_insert_guard\"\n ],\n \"tables\": [\n \"radroots_event_store_source_capacity_v1\"\n ],\n \"fts5_tables\": []\n }\n },\n \"source_maintenance\": {\n \"version\": 1,\n \"event_contract_registry_version\": 7,\n \"capacity_authority_id\": \"radroots_event_store_source_capacity_v1\",\n \"accounting\": {\n \"algorithm\": \"sqlite_cast_blob_octet_sum_v1\",\n \"raw_event_columns\": [\n \"event_id\",\n \"pubkey\",\n \"tags_json\",\n \"content\",\n \"sig\",\n \"raw_json\"\n ],\n \"raw_tag_columns\": [\n \"event_id\",\n \"tag_name\",\n \"tag_value\",\n \"tag_json\"\n ],\n \"nullable_raw_tag_columns\": [\n \"tag_value\"\n ]\n },\n \"limits\": {\n \"raw_events\": 25000,\n \"raw_tags\": 250000,\n \"raw_event_text_bytes\": 67108864,\n \"raw_tag_text_bytes\": 33554432,\n \"retained_source_generations\": 8\n },\n \"reopen_validation\": {\n \"mode\": \"bounded_full_raw_recount_v1\",\n \"raw_event_rejection_scan_bound\": 25001,\n \"raw_tag_rejection_scan_bound\": 250001,\n \"generation_history_validation\": \"bounded_count_plus_active_ordinal_v1\",\n \"retained_generation_rejection_scan_bound\": 9\n },\n \"rebuild_seal\": {\n \"nip09_hook_id\": \"nip09_reconciliation_v1\",\n \"nip09_manifest_sha256\": \"74af832420ffbaa9805e89df3c0b34f126a443e1598f757e3372f407f9003b77\",\n \"food_hook_id\": \"food_availability_projection_v1\",\n \"food_manifest_sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"food_scope_fingerprint_sha256\": \"8b63c5ddc48a2cc7db69295238b96d5f814dba50427c80b4d0079f061e6d3de0\",\n \"active_generation_authority\": \"radroots_event_store_source_state\",\n \"marker_close_authority\": \"radroots_event_store_source_capacity_marker_close_guard\"\n }\n },\n \"entry_points\": [\n {\n \"role\": \"migration_registry\",\n \"rust_path\": \"radroots_event_store::migrations::EVENT_STORE_MIGRATIONS[3]\"\n },\n {\n \"role\": \"migration_apply_hook\",\n \"rust_path\": \"radroots_event_store::schema::apply_migration_hook\"\n },\n {\n \"role\": \"migration_validation_hook\",\n \"rust_path\": \"radroots_event_store::schema::validate_migration_hook_state\"\n },\n {\n \"role\": \"capacity_query\",\n \"rust_path\": \"radroots_event_store::RadrootsEventStore::source_capacity_v1\"\n },\n {\n \"role\": \"raw_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_unique_raw_source_append_v1\"\n },\n {\n \"role\": \"raw_append_advance\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::advance_source_capacity_after_insert_v1\"\n },\n {\n \"role\": \"generation_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_source_generation_append_v1\"\n },\n {\n \"role\": \"generation_rebuild_bind\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::bind_source_capacity_to_generation_v1\"\n },\n {\n \"role\": \"sqlite_encoding_preflight\",\n \"rust_path\": \"radroots_event_store::store::validate_main_database_encoding\"\n },\n {\n \"role\": \"source_generation_history_rollback_guard\",\n \"rust_path\": \"radroots_event_store::schema::validate_rollback_preserves_source_generation_history\"\n },\n {\n \"role\": \"result_vector_executor\",\n \"rust_path\": \"source_maintenance_v1_result_vector\"\n }\n ],\n \"source_files\": [\n {\n \"role\": \"workspace_dependency_authority\",\n \"path\": \"Cargo.toml\",\n \"byte_length\": 12411,\n \"sha256\": \"cad96f080b0cc9ac7ffda703c8807540c9cf89f47a2299b141e6e7949714bf9f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_currency_value_authority\",\n \"path\": \"crates/core/src/currency.rs\",\n \"byte_length\": 4142,\n \"sha256\": \"42e6da2d8d2fdd6955dc2d83e98dd00266e02bc910b69923b117662c05089d14\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_decimal_value_authority\",\n \"path\": \"crates/core/src/decimal.rs\",\n \"byte_length\": 7839,\n \"sha256\": \"4b1d681a92d7a9e074bee7e1f20eb1499bb05bfc6528a6c9ba69fa7d00416550\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_money_value_authority\",\n \"path\": \"crates/core/src/money.rs\",\n \"byte_length\": 8025,\n \"sha256\": \"3f30bc21e21951a62fdc5d4033736ed42a883df590e8403940775bd2edfeebc5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_percent_value_authority\",\n \"path\": \"crates/core/src/percent.rs\",\n \"byte_length\": 2652,\n \"sha256\": \"42ceab109881329f3539d06a0ee0381ab4359985fe185a18d58a9c9558c5eb7f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_value_authority\",\n \"path\": \"crates/core/src/quantity.rs\",\n \"byte_length\": 6785,\n \"sha256\": \"d14e619da3829cdaf3ca7385fb892d330c286c1e21455c135c37518cbb588b5f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_price_value_authority\",\n \"path\": \"crates/core/src/quantity_price.rs\",\n \"byte_length\": 7061,\n \"sha256\": \"d60f70377099470ed283f45868a5119d7bb46143cb250b5082a4c038ba7f6cdc\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_unit_value_authority\",\n \"path\": \"crates/core/src/unit.rs\",\n \"byte_length\": 10023,\n \"sha256\": \"550e9582b6a5290aac3f637b83853b21fefcbab92c432d067c83448f4b2fe3e9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_public_surface\",\n \"path\": \"crates/blossom/src/lib.rs\",\n \"byte_length\": 510,\n \"sha256\": \"a4dfcbd193457c50c1b0fbfc91bb547981ecd3a6e23cfc6f1030edfb511dc58c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_authorization_authority\",\n \"path\": \"crates/blossom/src/authorization.rs\",\n \"byte_length\": 39360,\n \"sha256\": \"461947b30516315a342b3b5697599f701d27050888f29f67f5d7a7ad4afa4c28\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_descriptor_authority\",\n \"path\": \"crates/blossom/src/descriptor.rs\",\n \"byte_length\": 13702,\n \"sha256\": \"b2a4ffa760256e1316f70e012d200e2b5f4afd8ede771b85a5147f96b247f599\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_error_authority\",\n \"path\": \"crates/blossom/src/error.rs\",\n \"byte_length\": 17229,\n \"sha256\": \"02af55beacf437040be17d1fc3271d6e2a62915f656ec2dbe06247deef83f95a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_hash_authority\",\n \"path\": \"crates/blossom/src/hash.rs\",\n \"byte_length\": 11006,\n \"sha256\": \"45eca0d81ac0f46c305a32f81aafcdd90b14679d0614f72862289a6e5d4cb08f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_media_type_authority\",\n \"path\": \"crates/blossom/src/media_type.rs\",\n \"byte_length\": 2368,\n \"sha256\": \"69db5c0f9fcdd7dbe1f22daa499f65603ae4ce198083dab2c73a36813fa18a46\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_url_authority\",\n \"path\": \"crates/blossom/src/url.rs\",\n \"byte_length\": 14580,\n \"sha256\": \"342c995fcf620e5fb32461a7d8276f5668ba2662890bd32d12780cc9121c6451\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_facade\",\n \"path\": \"crates/event/src/contract.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_registry_v7_authority\",\n \"path\": \"crates/event/src/contract/registry_v7.rs\",\n \"byte_length\": 155739,\n \"sha256\": \"64f5a67f8a54f364287210a3f717d9124bc1cb2b9c5fc67e9ff0e40acd7bd216\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_envelope_authority\",\n \"path\": \"crates/event/src/envelope.rs\",\n \"byte_length\": 31127,\n \"sha256\": \"df22443b825e9ad3da1640029ca6ba9c2ef18a36e14d713bb167766be8ffb439\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_facade\",\n \"path\": \"crates/event/src/event_head.rs\",\n \"byte_length\": 68,\n \"sha256\": \"cb52f6006f7ecd862707d6b048f9fc407e0cd5ebcd3091b3c54e195ffa5cba64\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_v1_authority\",\n \"path\": \"crates/event/src/event_head/v1.rs\",\n \"byte_length\": 7515,\n \"sha256\": \"ff3236d2d1628aad5674e0d9a3d607b947190a1cf0020143a139cc28413ad36e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_ids_authority\",\n \"path\": \"crates/event/src/ids.rs\",\n \"byte_length\": 51018,\n \"sha256\": \"c420320528bac5a8381382cf33d5bed87037a2c753c0495bbc1c74a49de2756d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_trade_authority\",\n \"path\": \"crates/event/src/trade.rs\",\n \"byte_length\": 68354,\n \"sha256\": \"a26ad42c55c82ae6c646ac2678819cff26bd8325e53cd56018819bfd14269e33\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_kinds_authority\",\n \"path\": \"crates/event/src/kinds.rs\",\n \"byte_length\": 34961,\n \"sha256\": \"8b3ce6193cab1f7e1587d0c1b2880a81b0aa43c77d89671b602772db37edcebf\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_tags_authority\",\n \"path\": \"crates/event/src/tags.rs\",\n \"byte_length\": 4530,\n \"sha256\": \"cb8f12a639fc72d9238e32495d9e67e928e6627fb8c94ec0a58c5d95e7373cd6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_draft_authority\",\n \"path\": \"crates/event/src/draft.rs\",\n \"byte_length\": 58691,\n \"sha256\": \"b3b816bd195eb3269bf696c334d8cd51b406ea305f86cd496fe81fd5e8bcd513\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_calendar_authority\",\n \"path\": \"crates/event/src/calendar.rs\",\n \"byte_length\": 103814,\n \"sha256\": \"f7320e27daa113bff6fcc5da837bcce7d3b67c4df5fb9d9434e96f6ada753d6a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_classified_listing_authority\",\n \"path\": \"crates/event/src/classified_listing.rs\",\n \"byte_length\": 8226,\n \"sha256\": \"745aa08953a6243ea1afad22aa63e44f6746f2f243ab0d6b617d6ab64fa58749\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_profile_authority\",\n \"path\": \"crates/event/src/profile.rs\",\n \"byte_length\": 18848,\n \"sha256\": \"7c7ac7f4926938f5cb5530be5450e5e23f18f216bdb222150fbdd451cec20d5c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_post_authority\",\n \"path\": \"crates/event/src/post.rs\",\n \"byte_length\": 22738,\n \"sha256\": \"7ed4a4c39bed40b134369d16e1fc35ab2c7d27ee300dd36d367025def2283b39\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_comment_authority\",\n \"path\": \"crates/event/src/comment.rs\",\n \"byte_length\": 36411,\n \"sha256\": \"9f3e1b6457945a2defb1a1cf15d2b0230c1a0d2b72861fb5881859cd936299c7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_food_availability_authority\",\n \"path\": \"crates/event/src/food_availability.rs\",\n \"byte_length\": 45429,\n \"sha256\": \"b4357dfb6954631673b6cd58794015da29303cb54ce8405a57ad543870036c9e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_deletion_authority\",\n \"path\": \"crates/event/src/deletion.rs\",\n \"byte_length\": 31177,\n \"sha256\": \"eaf5eac81cd90039dd2c8f5a1f647d7916f961841d51887f1c5a679947bc816a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_relay_hint_authority\",\n \"path\": \"crates/event/src/relay_hint.rs\",\n \"byte_length\": 14019,\n \"sha256\": \"f50a8fddd1ec9996a452f944ebc59ca4217fc8b28d90be33c2cca925dc3465d0\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_media_authority\",\n \"path\": \"crates/event/src/media.rs\",\n \"byte_length\": 3521,\n \"sha256\": \"8f670e634452872a525efdad0cb9480288885c62f22a9b9915a67a7e7965651c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_social_authority\",\n \"path\": \"crates/event/src/social.rs\",\n \"byte_length\": 5483,\n \"sha256\": \"1b89c6d0954822e7f1e778479ed43694e88929bcd5310ee066f8f4ba6505045a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_facade\",\n \"path\": \"crates/event/src/wire.rs\",\n \"byte_length\": 68,\n \"sha256\": \"cb52f6006f7ecd862707d6b048f9fc407e0cd5ebcd3091b3c54e195ffa5cba64\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_v1_authority\",\n \"path\": \"crates/event/src/wire/v1.rs\",\n \"byte_length\": 22133,\n \"sha256\": \"cc28c8cbd962b32ed2efe43e84c1e0d1ea3bd89636efff2d5553714072fce1a4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_facade\",\n \"path\": \"crates/event_codec/src/verification.rs\",\n \"byte_length\": 253,\n \"sha256\": \"b49a32df605035c87f295c0a151140d43d2e588c5e11b05183e5fc92993dae0e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_v1_authority\",\n \"path\": \"crates/event_codec/src/verification/v1.rs\",\n \"byte_length\": 8649,\n \"sha256\": \"1ef3d12f90eb7d35f4cafe36c2cc0bbd33731df278b58c9062fb13934e2a98c8\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_registry_v7_admission_authority\",\n \"path\": \"crates/event_codec/src/admission/registry_v7.rs\",\n \"byte_length\": 5350,\n \"sha256\": \"755e63dd7ad1115c219e5a3ea540bef67d2770fc9f2a5aa6ba97c925c99bdced\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_inbound_facade\",\n \"path\": \"crates/event_codec/src/profile/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/profile/inbound/registry_v7.rs\",\n \"byte_length\": 10394,\n \"sha256\": \"c37c225ea8153beb098a39c4a56203aa03382af12646f40cd3f8628663700702\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_inbound_facade\",\n \"path\": \"crates/event_codec/src/post/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/post/inbound/registry_v7.rs\",\n \"byte_length\": 16016,\n \"sha256\": \"bc0bb839c28f3588f05544c846c987528ea3934c75305fef70af8e027a97cc58\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_inbound_facade\",\n \"path\": \"crates/event_codec/src/reply/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/reply/inbound/registry_v7.rs\",\n \"byte_length\": 26812,\n \"sha256\": \"a72e9721ed13addca2a63942ac9e43c1daa8f1d1457eef08ec6a3ee5eb8872c5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_inbound_facade\",\n \"path\": \"crates/event_codec/src/comment/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/comment/inbound/registry_v7.rs\",\n \"byte_length\": 53783,\n \"sha256\": \"30d1b0155963b25d64685ddd042f423ef7707b111dc56ead15bd468e0433a432\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_facade\",\n \"path\": \"crates/event_codec/src/deletion/mod.rs\",\n \"byte_length\": 115,\n \"sha256\": \"17d56f82ddb0a86bc97abbcf3e037fe460ea31fff2548f527be4bf004dac3a95\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_reconciliation_v1_authority\",\n \"path\": \"crates/event_codec/src/deletion/reconciliation_v1.rs\",\n \"byte_length\": 38469,\n \"sha256\": \"3e06d1fa923ca5faabb1e95338c35650b0e20b078ff5c972e35ab845d8b7476c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_inbound_facade\",\n \"path\": \"crates/event_codec/src/food_availability/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/food_availability/inbound/registry_v7.rs\",\n \"byte_length\": 26764,\n \"sha256\": \"a5786cff942b7b0038f741afb5b9a19230ac58cb57a5ba747b32e1b95d414e15\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_job_traits_authority\",\n \"path\": \"crates/event_codec/src/job/traits.rs\",\n \"byte_length\": 5067,\n \"sha256\": \"de0b3f67177a48bd8e3c62534850103d09b7be099e80e88a9583363db705fcbb\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_dependency_authority\",\n \"path\": \"crates/event_store/Cargo.toml\",\n \"byte_length\": 1567,\n \"sha256\": \"46e03d3c758e1116638021a0499dfa588b4266498e0d50cfa758f9bef9bcfeb3\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_error_and_limits\",\n \"path\": \"crates/event_store/src/error.rs\",\n \"byte_length\": 19517,\n \"sha256\": \"4bf24ee1aa6117597ab467b1379a05549b79024213dc7622a2d276bc26fffab4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"generated_descriptor_registration\",\n \"path\": \"crates/event_store/src/generated.rs\",\n \"byte_length\": 144,\n \"sha256\": \"6b0a8d6f249bd4fc3f878d37cb5e418680f0f1be2d9eec2518dedf03efc47121\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_surface\",\n \"path\": \"crates/event_store/src/lib.rs\",\n \"byte_length\": 3844,\n \"sha256\": \"3cd9653bcb752fb3c4442d4904b98a0a6208011a9a238125b7b7073d7f4e312b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"migration_registry\",\n \"path\": \"crates/event_store/src/migrations.rs\",\n \"byte_length\": 73585,\n \"sha256\": \"a47477d04759ec6e71d14d3d05459864eae78fce0e3c2481a1b03147f04637f7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_model_public_surface\",\n \"path\": \"crates/event_store/src/model.rs\",\n \"byte_length\": 33596,\n \"sha256\": \"98b1742ffb517f66f8e9edefa61d1c3cf3e22be5985202c6c340310a98c72c8e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_model\",\n \"path\": \"crates/event_store/src/model/reconciliation_v1.rs\",\n \"byte_length\": 11138,\n \"sha256\": \"8a26bc373035878ef9b41767ceea7b681896e17d88bedce118de1d622125e1d6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_ingest_model\",\n \"path\": \"crates/event_store/src/model/ingest_reconciliation_v1.rs\",\n \"byte_length\": 1626,\n \"sha256\": \"47bf13b3fc0f8a913a660f7d655413de0f6b90568bc4acc510aa6bd741bab47b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_model\",\n \"path\": \"crates/event_store/src/model/addressable_transition_feed_v1.rs\",\n \"byte_length\": 22279,\n \"sha256\": \"f8ea454a0e998a3d433bba52d1ff9759a04fcb91db286cf2a2b10ef7d0057694\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_model\",\n \"path\": \"crates/event_store/src/model/food_availability_projection_v1.rs\",\n \"byte_length\": 17437,\n \"sha256\": \"6fdfc8c5eb3dd9c451fd47b2394af123b50f7fe6d74113f8777221a8fa31ba30\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_generation_rebuild_authority\",\n \"path\": \"crates/event_store/src/nip09/reconciliation_v1.rs\",\n \"byte_length\": 184499,\n \"sha256\": \"883f7695f71e76b28e2dbd428d0ddb821ff62ae319cde61f823c62203869742b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"schema_migration_and_reopen_authority\",\n \"path\": \"crates/event_store/src/schema.rs\",\n \"byte_length\": 146146,\n \"sha256\": \"93b060e80d3edd73f86208e4bf698fa9d53eaf1eeb04526c9261fb8b5726fb0d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_store_and_transaction_authority\",\n \"path\": \"crates/event_store/src/store.rs\",\n \"byte_length\": 395012,\n \"sha256\": \"5538b600fa5e457e10f23fe4e3a3c55496662098abaef2692108dc69c29aa3e6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_store\",\n \"path\": \"crates/event_store/src/store/addressable_transition_feed_v1.rs\",\n \"byte_length\": 40260,\n \"sha256\": \"46ea9c9786099c85150e4979c9dac6f84c1b85b8f30ec44aae6d25f9eb9bebc5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"current_visibility_store\",\n \"path\": \"crates/event_store/src/store/current_visibility_v1.rs\",\n \"byte_length\": 15786,\n \"sha256\": \"bf03f9fc45561366cc489daa400b39d65e68cc70aea2ef4a9cd7724224ca9514\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_store\",\n \"path\": \"crates/event_store/src/store/food_availability_projection_v1.rs\",\n \"byte_length\": 48970,\n \"sha256\": \"9e0e63d6ca74e49d8ec77f088927983aa4d89a5dae4c9f2d766159e70ed53534\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_extension\",\n \"path\": \"crates/event_store/src/store/post_core_extensions_v1.rs\",\n \"byte_length\": 6923,\n \"sha256\": \"3820dad764a0575c20f3dc80a2f850651a1e41ab767cf46305377bf800558699\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_storage\",\n \"path\": \"crates/event_store/src/store/post_core_storage_v1.rs\",\n \"byte_length\": 16897,\n \"sha256\": \"3fb583b06dbd2e4fde96346da21e6d4516e79c8a4a3889299a7dd736c882c5df\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"raw_ingest_capacity_authority\",\n \"path\": \"crates/event_store/src/store/protocol_reconciliation_v1.rs\",\n \"byte_length\": 30153,\n \"sha256\": \"de7c327d940d561f0818fade4dc79cabb0b5141e30e02574c702b6f109aee392\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_protocol_storage\",\n \"path\": \"crates/event_store/src/store/protocol_storage_v1.rs\",\n \"byte_length\": 10929,\n \"sha256\": \"20de4436945bac0d8cbeca0ae6cd95232a92da8cd6be6ae6beacba17fe50d658\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_runtime\",\n \"path\": \"crates/event_store/src/source_maintenance_v1.rs\",\n \"byte_length\": 51849,\n \"sha256\": \"db43df5849c811d767a87bfa7884ce499400d0b169734e18242542a4415b3a51\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_food_result_vector_executor\",\n \"path\": \"crates/event_store/tests/food_availability_projection_v1_result_vector.rs\",\n \"byte_length\": 34061,\n \"sha256\": \"7cdbdda875a876482a061bcd8886d9ba56c10fab0fa76a07d73d0f368324b545\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"artifact_transaction_authority\",\n \"path\": \"tools/xtask/src/contract/artifact_bundle.rs\",\n \"byte_length\": 38279,\n \"sha256\": \"f326ea57b56d40135f95b6b1e15961f66eed363337180a6d12a5ea903e1a9a29\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_successor_governance\",\n \"path\": \"tools/xtask/src/contract/food_availability_projection.rs\",\n \"byte_length\": 197115,\n \"sha256\": \"a9f6553e26c10e255bbfda158989ecead2698e3e791ce577e290ffa88231692b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"transitive_predecessor_membership_governance\",\n \"path\": \"tools/xtask/src/contract/nip09_reconciliation.rs\",\n \"byte_length\": 836171,\n \"sha256\": \"a4f34441a90334f797d3542e4b0e009d0cfe0b23a1835e9984bd788fda4f493a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_governance\",\n \"path\": \"tools/xtask/src/contract/source_maintenance.rs\",\n \"byte_length\": 191437,\n \"sha256\": \"bd4682f287b777b2ae1348358511a524a4d2294a16e526553c08f220ca37a001\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 506277,\n \"sha256\": \"3a06ed79b5b342dce5b6582fc5c58311378a2dba97e5a8c74361f0186886023d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"xtask_dispatch_and_release_preflight\",\n \"path\": \"tools/xtask/src/main.rs\",\n \"byte_length\": 16426,\n \"sha256\": \"abc24896c6c740c10811a3e003ec4be8ebdb376a7b6b01445bfa7a5889f094a7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n ],\n \"public_api\": {\n \"inherited_predecessor_symbols\": [\n \"RADROOTS_ADDRESSABLE_TRANSITION_CURSOR_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_D_TAG_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_FEED_VERSION_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_LIMIT_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_RAW_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_SCAN_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_SCOPE_KIND_MAX_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_APPLY_PAGE_LIMIT_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_VERSION_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_BYTES_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_TERMS_V1\",\n \"RadrootsAddressableTransitionCauseV1\",\n \"RadrootsAddressableTransitionCoordinateV1\",\n \"RadrootsAddressableTransitionCursorV1\",\n \"RadrootsAddressableTransitionEventReferenceV1\",\n \"RadrootsAddressableTransitionOriginV1\",\n \"RadrootsAddressableTransitionPageV1\",\n \"RadrootsAddressableTransitionRawHeadDecisionV1\",\n \"RadrootsAddressableTransitionScopeFingerprintV1\",\n \"RadrootsAddressableTransitionScopeV1\",\n \"RadrootsAddressableTransitionV1\",\n \"RadrootsAddressableTransitionVisibilityV1\",\n \"RadrootsCurrentEventVisibilityV1\",\n \"RadrootsCurrentVisibilityDecisionV1\",\n \"RadrootsFoodAvailabilitySearchQueryV1\",\n \"RadrootsFoodAvailabilityStatusFilterV1\",\n \"RadrootsNip09SuppressionEvidenceV1\",\n \"RadrootsNip09SuppressionOutcome\",\n \"RadrootsNip09SuppressionReason\",\n \"RadrootsStoreProducedCanonicalEventV1\",\n \"RadrootsStoredFoodAvailabilityImageV1\",\n \"RadrootsStoredFoodAvailabilityV1\"\n ],\n \"added_symbols\": [\n \"RADROOTS_EVENT_STORE_RAW_EVENT_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_EVENT_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RETAINED_SOURCE_GENERATION_LIMIT_V1\",\n \"RadrootsEventStoreSourceCapacityResourceV1\",\n \"RadrootsEventStoreSourceCapacityV1\"\n ],\n \"methods\": [\n \"RadrootsEventStore::source_capacity_v1\",\n \"RadrootsEventStoreSourceCapacityResourceV1::as_str\",\n \"RadrootsEventStoreSourceCapacityV1::source_generation\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_high_water_seq\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_count\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_limit\"\n ],\n \"error_variants\": [\n \"SourceCapacityExceeded\",\n \"SourceGenerationHistoryLimitReached\",\n \"PersistedEphemeralRawEvent\",\n \"SourceCapacityStateDrift\",\n \"SqliteMainDatabaseEncodingNotUtf8\",\n \"RollbackWouldDiscardSourceGenerationHistory\"\n ],\n \"removed_symbols\": [\n \"RadrootsEventStoreReconciliationResource\",\n \"RadrootsEventStoreError::ReconciliationCapacityExceeded\"\n ],\n \"breaking_replacements\": [\n {\n \"removed\": \"RadrootsEventStoreReconciliationResource\",\n \"replacement\": \"RadrootsEventStoreSourceCapacityResourceV1\"\n },\n {\n \"removed\": \"RadrootsEventStoreError::ReconciliationCapacityExceeded\",\n \"replacement\": \"RadrootsEventStoreError::SourceCapacityExceeded\"\n }\n ]\n },\n \"result_vector\": {\n \"canonical_path\": \"contracts/conformance/vectors/event_store/source_maintenance.v1.json\",\n \"mirror_path\": \"crates/event_store/tests/fixtures/source_maintenance.v1.json\",\n \"byte_length\": 16253,\n \"sha256\": \"997aba2604a2b9d199fb87dc9d07942ca50d91863aeadcf3eeacf16d191dd71f\",\n \"hash_algorithm\": \"sha256_bytes_v1\",\n \"executor_id\": \"radroots_event_store.source_maintenance_v1.result_vector_executor.v1\",\n \"executor_path\": \"crates/event_store/tests/source_maintenance_v1_result_vector.rs\",\n \"executor_test\": \"source_maintenance_v1_result_vector\",\n \"executor_byte_length\": 23510,\n \"executor_sha256\": \"a7487afdfe19fc5fc794811d0f0e6035203e1aabcf0a33a1d398f6b3555d38f3\",\n \"executor_hash_algorithm\": \"sha256_bytes_v1\"\n }\n}\n"; -pub(crate) const SOURCE_MAINTENANCE_MANIFEST_BYTE_LENGTH: usize = 31654; +pub(crate) const SOURCE_MAINTENANCE_MANIFEST_JSON: &str = "{\n \"schema_version\": 1,\n \"contract_id\": \"radroots_event_store.source_maintenance_v1\",\n \"hook_id\": \"source_maintenance_v1\",\n \"manifest_schema\": {\n \"path\": \"crates/event_store/contracts/source_maintenance_v1.manifest.schema.json\",\n \"byte_length\": 12315,\n \"sha256\": \"96ea98a5abf3348b61565954cd34f2c622411d8dd34205ef04134bbe0e3d3f2b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"predecessor\": {\n \"hook_id\": \"food_availability_projection_v1\",\n \"manifest\": {\n \"path\": \"crates/event_store/contracts/food_availability_projection_v1.manifest.json\",\n \"byte_length\": 17455,\n \"sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n },\n \"migration\": {\n \"version\": 4,\n \"name\": \"source_maintenance\",\n \"up\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.up.sql\",\n \"byte_length\": 19841,\n \"sha256\": \"ab2724188f8d08c897eebea2533a635e7c74282a25e84e4c0c37e78b08837a43\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"down\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.down.sql\",\n \"byte_length\": 5172,\n \"sha256\": \"fe44fd53c51545c08ea479b385e6781079dab70fc63da2a3c205d727a00ce860\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"schema_sha256\": \"074f85b663444ac150239ecd8441ea4a96ad83a798a55e22d2e5e2f7ee943a8c\",\n \"catalog\": {\n \"objects\": [\n \"radroots_event_store_source_capacity_delete_guard\",\n \"radroots_event_store_source_capacity_insert_guard\",\n \"radroots_event_store_source_capacity_marker_close_guard\",\n \"radroots_event_store_source_capacity_update_guard\",\n \"radroots_event_store_source_capacity_v1\",\n \"radroots_event_store_source_generation_capacity_advance\",\n \"radroots_event_store_source_generation_capacity_guard\"\n ],\n \"replaced_objects\": [\n \"radroots_event_store_food_availability_image_delete_guard\",\n \"radroots_event_store_food_availability_projection_delete_guard\",\n \"radroots_event_store_source_rebuild_marker_insert_guard\"\n ],\n \"tables\": [\n \"radroots_event_store_source_capacity_v1\"\n ],\n \"fts5_tables\": []\n }\n },\n \"source_maintenance\": {\n \"version\": 1,\n \"event_contract_registry_version\": 7,\n \"capacity_authority_id\": \"radroots_event_store_source_capacity_v1\",\n \"accounting\": {\n \"algorithm\": \"sqlite_cast_blob_octet_sum_v1\",\n \"raw_event_columns\": [\n \"event_id\",\n \"pubkey\",\n \"tags_json\",\n \"content\",\n \"sig\",\n \"raw_json\"\n ],\n \"raw_tag_columns\": [\n \"event_id\",\n \"tag_name\",\n \"tag_value\",\n \"tag_json\"\n ],\n \"nullable_raw_tag_columns\": [\n \"tag_value\"\n ]\n },\n \"limits\": {\n \"raw_events\": 25000,\n \"raw_tags\": 250000,\n \"raw_event_text_bytes\": 67108864,\n \"raw_tag_text_bytes\": 33554432,\n \"retained_source_generations\": 8\n },\n \"reopen_validation\": {\n \"mode\": \"bounded_full_raw_recount_v1\",\n \"raw_event_rejection_scan_bound\": 25001,\n \"raw_tag_rejection_scan_bound\": 250001,\n \"generation_history_validation\": \"bounded_count_plus_active_ordinal_v1\",\n \"retained_generation_rejection_scan_bound\": 9\n },\n \"rebuild_seal\": {\n \"nip09_hook_id\": \"nip09_reconciliation_v1\",\n \"nip09_manifest_sha256\": \"74af832420ffbaa9805e89df3c0b34f126a443e1598f757e3372f407f9003b77\",\n \"food_hook_id\": \"food_availability_projection_v1\",\n \"food_manifest_sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"food_scope_fingerprint_sha256\": \"8b63c5ddc48a2cc7db69295238b96d5f814dba50427c80b4d0079f061e6d3de0\",\n \"active_generation_authority\": \"radroots_event_store_source_state\",\n \"marker_close_authority\": \"radroots_event_store_source_capacity_marker_close_guard\"\n }\n },\n \"entry_points\": [\n {\n \"role\": \"migration_registry\",\n \"rust_path\": \"radroots_event_store::migrations::EVENT_STORE_MIGRATIONS[3]\"\n },\n {\n \"role\": \"migration_apply_hook\",\n \"rust_path\": \"radroots_event_store::schema::apply_migration_hook\"\n },\n {\n \"role\": \"migration_validation_hook\",\n \"rust_path\": \"radroots_event_store::schema::validate_migration_hook_state\"\n },\n {\n \"role\": \"capacity_query\",\n \"rust_path\": \"radroots_event_store::RadrootsEventStore::source_capacity_v1\"\n },\n {\n \"role\": \"raw_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_unique_raw_source_append_v1\"\n },\n {\n \"role\": \"raw_append_advance\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::advance_source_capacity_after_insert_v1\"\n },\n {\n \"role\": \"generation_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_source_generation_append_v1\"\n },\n {\n \"role\": \"generation_rebuild_bind\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::bind_source_capacity_to_generation_v1\"\n },\n {\n \"role\": \"sqlite_encoding_preflight\",\n \"rust_path\": \"radroots_event_store::store::validate_main_database_encoding\"\n },\n {\n \"role\": \"source_generation_history_rollback_guard\",\n \"rust_path\": \"radroots_event_store::schema::validate_rollback_preserves_source_generation_history\"\n },\n {\n \"role\": \"result_vector_executor\",\n \"rust_path\": \"source_maintenance_v1_result_vector\"\n }\n ],\n \"source_files\": [\n {\n \"role\": \"workspace_dependency_authority\",\n \"path\": \"Cargo.toml\",\n \"byte_length\": 12411,\n \"sha256\": \"cad96f080b0cc9ac7ffda703c8807540c9cf89f47a2299b141e6e7949714bf9f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_currency_value_authority\",\n \"path\": \"crates/core/src/currency.rs\",\n \"byte_length\": 4142,\n \"sha256\": \"42e6da2d8d2fdd6955dc2d83e98dd00266e02bc910b69923b117662c05089d14\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_decimal_value_authority\",\n \"path\": \"crates/core/src/decimal.rs\",\n \"byte_length\": 7839,\n \"sha256\": \"4b1d681a92d7a9e074bee7e1f20eb1499bb05bfc6528a6c9ba69fa7d00416550\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_money_value_authority\",\n \"path\": \"crates/core/src/money.rs\",\n \"byte_length\": 8025,\n \"sha256\": \"3f30bc21e21951a62fdc5d4033736ed42a883df590e8403940775bd2edfeebc5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_percent_value_authority\",\n \"path\": \"crates/core/src/percent.rs\",\n \"byte_length\": 2652,\n \"sha256\": \"42ceab109881329f3539d06a0ee0381ab4359985fe185a18d58a9c9558c5eb7f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_value_authority\",\n \"path\": \"crates/core/src/quantity.rs\",\n \"byte_length\": 6785,\n \"sha256\": \"d14e619da3829cdaf3ca7385fb892d330c286c1e21455c135c37518cbb588b5f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_price_value_authority\",\n \"path\": \"crates/core/src/quantity_price.rs\",\n \"byte_length\": 7061,\n \"sha256\": \"d60f70377099470ed283f45868a5119d7bb46143cb250b5082a4c038ba7f6cdc\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_unit_value_authority\",\n \"path\": \"crates/core/src/unit.rs\",\n \"byte_length\": 10023,\n \"sha256\": \"550e9582b6a5290aac3f637b83853b21fefcbab92c432d067c83448f4b2fe3e9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_public_surface\",\n \"path\": \"crates/blossom/src/lib.rs\",\n \"byte_length\": 510,\n \"sha256\": \"a4dfcbd193457c50c1b0fbfc91bb547981ecd3a6e23cfc6f1030edfb511dc58c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_authorization_authority\",\n \"path\": \"crates/blossom/src/authorization.rs\",\n \"byte_length\": 39360,\n \"sha256\": \"461947b30516315a342b3b5697599f701d27050888f29f67f5d7a7ad4afa4c28\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_descriptor_authority\",\n \"path\": \"crates/blossom/src/descriptor.rs\",\n \"byte_length\": 13702,\n \"sha256\": \"b2a4ffa760256e1316f70e012d200e2b5f4afd8ede771b85a5147f96b247f599\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_error_authority\",\n \"path\": \"crates/blossom/src/error.rs\",\n \"byte_length\": 17229,\n \"sha256\": \"02af55beacf437040be17d1fc3271d6e2a62915f656ec2dbe06247deef83f95a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_hash_authority\",\n \"path\": \"crates/blossom/src/hash.rs\",\n \"byte_length\": 11006,\n \"sha256\": \"45eca0d81ac0f46c305a32f81aafcdd90b14679d0614f72862289a6e5d4cb08f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_media_type_authority\",\n \"path\": \"crates/blossom/src/media_type.rs\",\n \"byte_length\": 2368,\n \"sha256\": \"69db5c0f9fcdd7dbe1f22daa499f65603ae4ce198083dab2c73a36813fa18a46\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_url_authority\",\n \"path\": \"crates/blossom/src/url.rs\",\n \"byte_length\": 14580,\n \"sha256\": \"342c995fcf620e5fb32461a7d8276f5668ba2662890bd32d12780cc9121c6451\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_facade\",\n \"path\": \"crates/event/src/contract.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_registry_v7_authority\",\n \"path\": \"crates/event/src/contract/registry_v7.rs\",\n \"byte_length\": 155739,\n \"sha256\": \"64f5a67f8a54f364287210a3f717d9124bc1cb2b9c5fc67e9ff0e40acd7bd216\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_envelope_authority\",\n \"path\": \"crates/event/src/envelope.rs\",\n \"byte_length\": 31143,\n \"sha256\": \"2478ca91b4dedaf8b8aefe653971b70c56c4ee076dcbfdf7f59f97f86c03b748\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_facade\",\n \"path\": \"crates/event/src/event_head.rs\",\n \"byte_length\": 68,\n \"sha256\": \"cb52f6006f7ecd862707d6b048f9fc407e0cd5ebcd3091b3c54e195ffa5cba64\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_v1_authority\",\n \"path\": \"crates/event/src/event_head/v1.rs\",\n \"byte_length\": 7538,\n \"sha256\": \"fbc738b403afd3a186ab183ff5f457125a7fbfab01a51fd4b46c92a2543a0126\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_ids_authority\",\n \"path\": \"crates/event/src/ids.rs\",\n \"byte_length\": 51007,\n \"sha256\": \"7d5d52dcbdd2ec31d914999960fa58aa279a8d4f8bd380fb6050b9a1019ad2d8\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_trade_authority\",\n \"path\": \"crates/event/src/trade.rs\",\n \"byte_length\": 68373,\n \"sha256\": \"f72ea517bb2a634420ee7528e0fbd38e524c8a4c09e7be47ffc237d839d9b2ec\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_kinds_authority\",\n \"path\": \"crates/event/src/kinds.rs\",\n \"byte_length\": 34961,\n \"sha256\": \"8b3ce6193cab1f7e1587d0c1b2880a81b0aa43c77d89671b602772db37edcebf\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_tags_authority\",\n \"path\": \"crates/event/src/tags.rs\",\n \"byte_length\": 4530,\n \"sha256\": \"cb8f12a639fc72d9238e32495d9e67e928e6627fb8c94ec0a58c5d95e7373cd6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_draft_authority\",\n \"path\": \"crates/event/src/draft.rs\",\n \"byte_length\": 58707,\n \"sha256\": \"c21e1b2e9cf6bfb4e007a32772ba59452deef0c9ba5f652992159e85fe7ccb24\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_calendar_authority\",\n \"path\": \"crates/event/src/calendar.rs\",\n \"byte_length\": 103837,\n \"sha256\": \"928e5a3df61ab08565cecf20a5045352236bc3d807127f1cd66294d60ee65621\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_classified_listing_authority\",\n \"path\": \"crates/event/src/classified_listing.rs\",\n \"byte_length\": 8226,\n \"sha256\": \"745aa08953a6243ea1afad22aa63e44f6746f2f243ab0d6b617d6ab64fa58749\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_profile_authority\",\n \"path\": \"crates/event/src/profile.rs\",\n \"byte_length\": 18848,\n \"sha256\": \"7c7ac7f4926938f5cb5530be5450e5e23f18f216bdb222150fbdd451cec20d5c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_post_authority\",\n \"path\": \"crates/event/src/post.rs\",\n \"byte_length\": 22738,\n \"sha256\": \"7ed4a4c39bed40b134369d16e1fc35ab2c7d27ee300dd36d367025def2283b39\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_comment_authority\",\n \"path\": \"crates/event/src/comment.rs\",\n \"byte_length\": 36434,\n \"sha256\": \"ec944bd868deb71215c915a35e79ff26715317fdb62c0fbbb5f1d0993f769c38\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_food_availability_authority\",\n \"path\": \"crates/event/src/food_availability.rs\",\n \"byte_length\": 45429,\n \"sha256\": \"b4357dfb6954631673b6cd58794015da29303cb54ce8405a57ad543870036c9e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_deletion_authority\",\n \"path\": \"crates/event/src/deletion.rs\",\n \"byte_length\": 31177,\n \"sha256\": \"eaf5eac81cd90039dd2c8f5a1f647d7916f961841d51887f1c5a679947bc816a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_relay_hint_authority\",\n \"path\": \"crates/event/src/relay_hint.rs\",\n \"byte_length\": 14019,\n \"sha256\": \"f50a8fddd1ec9996a452f944ebc59ca4217fc8b28d90be33c2cca925dc3465d0\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_media_authority\",\n \"path\": \"crates/event/src/media.rs\",\n \"byte_length\": 3521,\n \"sha256\": \"8f670e634452872a525efdad0cb9480288885c62f22a9b9915a67a7e7965651c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_social_authority\",\n \"path\": \"crates/event/src/social.rs\",\n \"byte_length\": 5483,\n \"sha256\": \"1b89c6d0954822e7f1e778479ed43694e88929bcd5310ee066f8f4ba6505045a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_facade\",\n \"path\": \"crates/event/src/wire.rs\",\n \"byte_length\": 68,\n \"sha256\": \"cb52f6006f7ecd862707d6b048f9fc407e0cd5ebcd3091b3c54e195ffa5cba64\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_v1_authority\",\n \"path\": \"crates/event/src/wire/v1.rs\",\n \"byte_length\": 22133,\n \"sha256\": \"cc28c8cbd962b32ed2efe43e84c1e0d1ea3bd89636efff2d5553714072fce1a4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_facade\",\n \"path\": \"crates/event_codec/src/verification.rs\",\n \"byte_length\": 253,\n \"sha256\": \"b49a32df605035c87f295c0a151140d43d2e588c5e11b05183e5fc92993dae0e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_v1_authority\",\n \"path\": \"crates/event_codec/src/verification/v1.rs\",\n \"byte_length\": 8649,\n \"sha256\": \"1ef3d12f90eb7d35f4cafe36c2cc0bbd33731df278b58c9062fb13934e2a98c8\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_registry_v7_admission_authority\",\n \"path\": \"crates/event_codec/src/admission/registry_v7.rs\",\n \"byte_length\": 5350,\n \"sha256\": \"755e63dd7ad1115c219e5a3ea540bef67d2770fc9f2a5aa6ba97c925c99bdced\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_inbound_facade\",\n \"path\": \"crates/event_codec/src/profile/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/profile/inbound/registry_v7.rs\",\n \"byte_length\": 10394,\n \"sha256\": \"c37c225ea8153beb098a39c4a56203aa03382af12646f40cd3f8628663700702\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_inbound_facade\",\n \"path\": \"crates/event_codec/src/post/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/post/inbound/registry_v7.rs\",\n \"byte_length\": 16016,\n \"sha256\": \"bc0bb839c28f3588f05544c846c987528ea3934c75305fef70af8e027a97cc58\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_inbound_facade\",\n \"path\": \"crates/event_codec/src/reply/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/reply/inbound/registry_v7.rs\",\n \"byte_length\": 26812,\n \"sha256\": \"a72e9721ed13addca2a63942ac9e43c1daa8f1d1457eef08ec6a3ee5eb8872c5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_inbound_facade\",\n \"path\": \"crates/event_codec/src/comment/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/comment/inbound/registry_v7.rs\",\n \"byte_length\": 53783,\n \"sha256\": \"30d1b0155963b25d64685ddd042f423ef7707b111dc56ead15bd468e0433a432\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_facade\",\n \"path\": \"crates/event_codec/src/deletion/mod.rs\",\n \"byte_length\": 115,\n \"sha256\": \"17d56f82ddb0a86bc97abbcf3e037fe460ea31fff2548f527be4bf004dac3a95\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_reconciliation_v1_authority\",\n \"path\": \"crates/event_codec/src/deletion/reconciliation_v1.rs\",\n \"byte_length\": 38469,\n \"sha256\": \"3e06d1fa923ca5faabb1e95338c35650b0e20b078ff5c972e35ab845d8b7476c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_inbound_facade\",\n \"path\": \"crates/event_codec/src/food_availability/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/food_availability/inbound/registry_v7.rs\",\n \"byte_length\": 26764,\n \"sha256\": \"a5786cff942b7b0038f741afb5b9a19230ac58cb57a5ba747b32e1b95d414e15\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_job_traits_authority\",\n \"path\": \"crates/event_codec/src/job/traits.rs\",\n \"byte_length\": 5067,\n \"sha256\": \"de0b3f67177a48bd8e3c62534850103d09b7be099e80e88a9583363db705fcbb\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_dependency_authority\",\n \"path\": \"crates/event_store/Cargo.toml\",\n \"byte_length\": 1567,\n \"sha256\": \"46e03d3c758e1116638021a0499dfa588b4266498e0d50cfa758f9bef9bcfeb3\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_error_and_limits\",\n \"path\": \"crates/event_store/src/error.rs\",\n \"byte_length\": 19517,\n \"sha256\": \"4bf24ee1aa6117597ab467b1379a05549b79024213dc7622a2d276bc26fffab4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"generated_descriptor_registration\",\n \"path\": \"crates/event_store/src/generated.rs\",\n \"byte_length\": 144,\n \"sha256\": \"6b0a8d6f249bd4fc3f878d37cb5e418680f0f1be2d9eec2518dedf03efc47121\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_surface\",\n \"path\": \"crates/event_store/src/lib.rs\",\n \"byte_length\": 3844,\n \"sha256\": \"3cd9653bcb752fb3c4442d4904b98a0a6208011a9a238125b7b7073d7f4e312b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"migration_registry\",\n \"path\": \"crates/event_store/src/migrations.rs\",\n \"byte_length\": 73585,\n \"sha256\": \"a47477d04759ec6e71d14d3d05459864eae78fce0e3c2481a1b03147f04637f7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_model_public_surface\",\n \"path\": \"crates/event_store/src/model.rs\",\n \"byte_length\": 33596,\n \"sha256\": \"98b1742ffb517f66f8e9edefa61d1c3cf3e22be5985202c6c340310a98c72c8e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_model\",\n \"path\": \"crates/event_store/src/model/reconciliation_v1.rs\",\n \"byte_length\": 11138,\n \"sha256\": \"8a26bc373035878ef9b41767ceea7b681896e17d88bedce118de1d622125e1d6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_ingest_model\",\n \"path\": \"crates/event_store/src/model/ingest_reconciliation_v1.rs\",\n \"byte_length\": 1626,\n \"sha256\": \"47bf13b3fc0f8a913a660f7d655413de0f6b90568bc4acc510aa6bd741bab47b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_model\",\n \"path\": \"crates/event_store/src/model/addressable_transition_feed_v1.rs\",\n \"byte_length\": 22279,\n \"sha256\": \"f8ea454a0e998a3d433bba52d1ff9759a04fcb91db286cf2a2b10ef7d0057694\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_model\",\n \"path\": \"crates/event_store/src/model/food_availability_projection_v1.rs\",\n \"byte_length\": 17437,\n \"sha256\": \"6fdfc8c5eb3dd9c451fd47b2394af123b50f7fe6d74113f8777221a8fa31ba30\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_generation_rebuild_authority\",\n \"path\": \"crates/event_store/src/nip09/reconciliation_v1.rs\",\n \"byte_length\": 185215,\n \"sha256\": \"575c9fb25b1f728144f5fe649007be705092d9b137f51fd8d771439871bfa09a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"nip09_successor_result_vector_executor\",\n \"path\": \"crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs\",\n \"byte_length\": 17983,\n \"sha256\": \"a1d919aa545d7c506d7ae8740445025732c2cda302790d243a4ff3d6cee898f6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"schema_migration_and_reopen_authority\",\n \"path\": \"crates/event_store/src/schema.rs\",\n \"byte_length\": 146146,\n \"sha256\": \"93b060e80d3edd73f86208e4bf698fa9d53eaf1eeb04526c9261fb8b5726fb0d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_store_and_transaction_authority\",\n \"path\": \"crates/event_store/src/store.rs\",\n \"byte_length\": 395012,\n \"sha256\": \"5538b600fa5e457e10f23fe4e3a3c55496662098abaef2692108dc69c29aa3e6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_store\",\n \"path\": \"crates/event_store/src/store/addressable_transition_feed_v1.rs\",\n \"byte_length\": 40260,\n \"sha256\": \"46ea9c9786099c85150e4979c9dac6f84c1b85b8f30ec44aae6d25f9eb9bebc5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"current_visibility_store\",\n \"path\": \"crates/event_store/src/store/current_visibility_v1.rs\",\n \"byte_length\": 15786,\n \"sha256\": \"bf03f9fc45561366cc489daa400b39d65e68cc70aea2ef4a9cd7724224ca9514\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_store\",\n \"path\": \"crates/event_store/src/store/food_availability_projection_v1.rs\",\n \"byte_length\": 48959,\n \"sha256\": \"a01ed95983481a8f0efdcc985635efba122fdbfafc226b7e0b30325af6b4de80\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_extension\",\n \"path\": \"crates/event_store/src/store/post_core_extensions_v1.rs\",\n \"byte_length\": 6923,\n \"sha256\": \"3820dad764a0575c20f3dc80a2f850651a1e41ab767cf46305377bf800558699\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_storage\",\n \"path\": \"crates/event_store/src/store/post_core_storage_v1.rs\",\n \"byte_length\": 16897,\n \"sha256\": \"3fb583b06dbd2e4fde96346da21e6d4516e79c8a4a3889299a7dd736c882c5df\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"raw_ingest_capacity_authority\",\n \"path\": \"crates/event_store/src/store/protocol_reconciliation_v1.rs\",\n \"byte_length\": 30153,\n \"sha256\": \"de7c327d940d561f0818fade4dc79cabb0b5141e30e02574c702b6f109aee392\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_protocol_storage\",\n \"path\": \"crates/event_store/src/store/protocol_storage_v1.rs\",\n \"byte_length\": 10929,\n \"sha256\": \"20de4436945bac0d8cbeca0ae6cd95232a92da8cd6be6ae6beacba17fe50d658\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_runtime\",\n \"path\": \"crates/event_store/src/source_maintenance_v1.rs\",\n \"byte_length\": 51849,\n \"sha256\": \"db43df5849c811d767a87bfa7884ce499400d0b169734e18242542a4415b3a51\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_food_result_vector_executor\",\n \"path\": \"crates/event_store/tests/food_availability_projection_v1_result_vector.rs\",\n \"byte_length\": 34061,\n \"sha256\": \"7cdbdda875a876482a061bcd8886d9ba56c10fab0fa76a07d73d0f368324b545\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"artifact_transaction_authority\",\n \"path\": \"tools/xtask/src/contract/artifact_bundle.rs\",\n \"byte_length\": 38279,\n \"sha256\": \"f326ea57b56d40135f95b6b1e15961f66eed363337180a6d12a5ea903e1a9a29\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_successor_governance\",\n \"path\": \"tools/xtask/src/contract/food_availability_projection.rs\",\n \"byte_length\": 198638,\n \"sha256\": \"66a703e5284d0ce91a95d519eee93bd9763d4ab24b7fe8ef39672064118ed391\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"transitive_predecessor_membership_governance\",\n \"path\": \"tools/xtask/src/contract/nip09_reconciliation.rs\",\n \"byte_length\": 836177,\n \"sha256\": \"7b1d13ca72fbe92dfa6c80d589528aca044bbfd6a960f21a187ccc1090a3cdc1\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_governance\",\n \"path\": \"tools/xtask/src/contract/source_maintenance.rs\",\n \"byte_length\": 193738,\n \"sha256\": \"c24c5a50b2467ecb4a260e29737e3cdf5fffe68028635da3d6ca486563a335fa\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 506277,\n \"sha256\": \"3a06ed79b5b342dce5b6582fc5c58311378a2dba97e5a8c74361f0186886023d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"xtask_dispatch_and_release_preflight\",\n \"path\": \"tools/xtask/src/main.rs\",\n \"byte_length\": 16426,\n \"sha256\": \"abc24896c6c740c10811a3e003ec4be8ebdb376a7b6b01445bfa7a5889f094a7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n ],\n \"public_api\": {\n \"inherited_predecessor_symbols\": [\n \"RADROOTS_ADDRESSABLE_TRANSITION_CURSOR_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_D_TAG_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_FEED_VERSION_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_LIMIT_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_RAW_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_SCAN_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_SCOPE_KIND_MAX_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_APPLY_PAGE_LIMIT_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_VERSION_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_BYTES_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_TERMS_V1\",\n \"RadrootsAddressableTransitionCauseV1\",\n \"RadrootsAddressableTransitionCoordinateV1\",\n \"RadrootsAddressableTransitionCursorV1\",\n \"RadrootsAddressableTransitionEventReferenceV1\",\n \"RadrootsAddressableTransitionOriginV1\",\n \"RadrootsAddressableTransitionPageV1\",\n \"RadrootsAddressableTransitionRawHeadDecisionV1\",\n \"RadrootsAddressableTransitionScopeFingerprintV1\",\n \"RadrootsAddressableTransitionScopeV1\",\n \"RadrootsAddressableTransitionV1\",\n \"RadrootsAddressableTransitionVisibilityV1\",\n \"RadrootsCurrentEventVisibilityV1\",\n \"RadrootsCurrentVisibilityDecisionV1\",\n \"RadrootsFoodAvailabilitySearchQueryV1\",\n \"RadrootsFoodAvailabilityStatusFilterV1\",\n \"RadrootsNip09SuppressionEvidenceV1\",\n \"RadrootsNip09SuppressionOutcome\",\n \"RadrootsNip09SuppressionReason\",\n \"RadrootsStoreProducedCanonicalEventV1\",\n \"RadrootsStoredFoodAvailabilityImageV1\",\n \"RadrootsStoredFoodAvailabilityV1\"\n ],\n \"added_symbols\": [\n \"RADROOTS_EVENT_STORE_RAW_EVENT_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_EVENT_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RETAINED_SOURCE_GENERATION_LIMIT_V1\",\n \"RadrootsEventStoreSourceCapacityResourceV1\",\n \"RadrootsEventStoreSourceCapacityV1\"\n ],\n \"methods\": [\n \"RadrootsEventStore::source_capacity_v1\",\n \"RadrootsEventStoreSourceCapacityResourceV1::as_str\",\n \"RadrootsEventStoreSourceCapacityV1::source_generation\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_high_water_seq\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_count\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_limit\"\n ],\n \"error_variants\": [\n \"SourceCapacityExceeded\",\n \"SourceGenerationHistoryLimitReached\",\n \"PersistedEphemeralRawEvent\",\n \"SourceCapacityStateDrift\",\n \"SqliteMainDatabaseEncodingNotUtf8\",\n \"RollbackWouldDiscardSourceGenerationHistory\"\n ],\n \"removed_symbols\": [\n \"RadrootsEventStoreReconciliationResource\",\n \"RadrootsEventStoreError::ReconciliationCapacityExceeded\"\n ],\n \"breaking_replacements\": [\n {\n \"removed\": \"RadrootsEventStoreReconciliationResource\",\n \"replacement\": \"RadrootsEventStoreSourceCapacityResourceV1\"\n },\n {\n \"removed\": \"RadrootsEventStoreError::ReconciliationCapacityExceeded\",\n \"replacement\": \"RadrootsEventStoreError::SourceCapacityExceeded\"\n }\n ]\n },\n \"result_vector\": {\n \"canonical_path\": \"contracts/conformance/vectors/event_store/source_maintenance.v1.json\",\n \"mirror_path\": \"crates/event_store/tests/fixtures/source_maintenance.v1.json\",\n \"byte_length\": 16253,\n \"sha256\": \"997aba2604a2b9d199fb87dc9d07942ca50d91863aeadcf3eeacf16d191dd71f\",\n \"hash_algorithm\": \"sha256_bytes_v1\",\n \"executor_id\": \"radroots_event_store.source_maintenance_v1.result_vector_executor.v1\",\n \"executor_path\": \"crates/event_store/tests/source_maintenance_v1_result_vector.rs\",\n \"executor_test\": \"source_maintenance_v1_result_vector\",\n \"executor_byte_length\": 23510,\n \"executor_sha256\": \"a7487afdfe19fc5fc794811d0f0e6035203e1aabcf0a33a1d398f6b3555d38f3\",\n \"executor_hash_algorithm\": \"sha256_bytes_v1\"\n }\n}\n"; +pub(crate) const SOURCE_MAINTENANCE_MANIFEST_BYTE_LENGTH: usize = 31971; pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SHA256: &str = - "267a332c0cfdc9dbc064c98dab23a41ea32ed7d2c2fe31b50777b1c92dbfba01"; + "368788cfd6e10a1fc7fb9c76c653b408f90ed0a8bb474606b139615c92a1cf43"; pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SCHEMA_VERSION: u32 = 1; pub(crate) const SOURCE_MAINTENANCE_CONTRACT_ID: &str = "radroots_event_store.source_maintenance_v1"; diff --git a/crates/event_store/src/nip09/reconciliation_v1.rs b/crates/event_store/src/nip09/reconciliation_v1.rs @@ -41,8 +41,28 @@ use sqlx::{Row, SqliteConnection}; use std::collections::{BTreeMap, BTreeSet}; #[cfg(test)] +#[path = "../../tests/support/nip09_reconciliation_v1_result_vector_v2.rs"] mod result_vector_executor; +#[cfg(test)] +const NIP09_RESULT_VECTOR_BYTES: &[u8] = + include_bytes!("../../tests/fixtures/nip09_reconciliation.v1.json"); +#[cfg(test)] +const NIP09_EVENT_STORE_V1_UP_SQL: &str = include_str!("../../migrations/0001_event_store.up.sql"); +#[cfg(test)] +const NIP09_V1_UP_SQL: &str = include_str!("../../migrations/0002_nip09.up.sql"); + +#[cfg(test)] +struct FixedResultVectorGeneration([u8; 32]); + +#[cfg(test)] +impl SourceGenerationProvider for FixedResultVectorGeneration { + fn fill_generation(&self, generation: &mut [u8; 32]) -> Result<(), RadrootsEventStoreError> { + generation.copy_from_slice(&self.0); + Ok(()) + } +} + const RECONCILIATION_SNAPSHOT_BATCH_SIZE: i64 = 512; const RECONCILIATION_SNAPSHOT_BATCH_LEN: usize = 512; const RECONCILIATION_SNAPSHOT_BATCH_COUNT: u64 = 512; diff --git a/crates/event_store/src/store/food_availability_projection_v1.rs b/crates/event_store/src/store/food_availability_projection_v1.rs @@ -528,7 +528,7 @@ pub(crate) async fn validate_food_availability_projection_hook_v1( actual_coordinates.push(( projection.pubkey().to_hex(), projection.identifier().as_str().to_owned(), - projection.event_id().to_hex().to_owned(), + projection.event_id().to_hex(), projection.event_seq(), i64_from_u64("food.created_at", projection.created_at())?, )); diff --git a/crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs b/crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs @@ -0,0 +1,513 @@ +#![forbid(unsafe_code)] + +use super::{ + EventAdmission, FixedResultVectorGeneration, NIP09_EVENT_STORE_V1_UP_SQL, + NIP09_RESULT_VECTOR_BYTES, NIP09_V1_UP_SQL, ReconciliationCapacityLimits, + ReconciliationProfile, apply_reconciliation_hook, validate_applied_hook_state, +}; +use crate::generated::nip09_reconciliation_manifest as nip09_manifest; +use crate::model::reconciliation_v1::{ + RadrootsEventAdmissionStatus, RadrootsEventIngest, StoredEventClass, tag_semantic_name, + tag_value_type_name, +}; +use radroots_event::kinds::KIND_LIST_SET_FOLLOW; +use radroots_event::wire::v1::RadrootsNip01EventWire; +use sha2::{Digest, Sha256}; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; +use sqlx::{Row, SqliteConnection, SqlitePool}; +use std::str::FromStr; + +pub(super) const RESULT_VECTOR_EXECUTOR_ID: &str = + "radroots_event_store.nip09_reconciliation_v1.result_vector_executor.v2"; + +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct Nip09ReconciliationVector { + schema_version: u32, + hook_id: String, + cases: Vec<Nip09ReconciliationVectorCase>, +} + +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct Nip09ReconciliationVectorCase { + id: String, + source_generation_hex: String, + input_events: Vec<Nip09ReconciliationVectorInput>, + expected: Nip09ReconciliationVectorExpected, +} + +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct Nip09ReconciliationVectorInput { + observed_at_ms: i64, + event: Nip09ReconciliationVectorEvent, +} + +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct Nip09ReconciliationVectorEvent { + id: String, + pubkey: String, + created_at: u64, + kind: u32, + tags: Vec<Vec<String>>, + content: String, + sig: String, +} + +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct Nip09ReconciliationVectorExpected { + raw_event_count: i64, + coordinate_count: i64, + request_count: i64, + event_target_count: i64, + address_target_count: i64, + transition_count: i64, + state: Nip09ReconciliationVectorState, +} + +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct Nip09ReconciliationVectorState { + kind: i64, + pubkey: String, + d_tag: String, + raw_head_event_id: String, + admission_status: String, + contract_id: String, + visibility: String, + nip09_outcome: String, + nip09_reason: String, + #[serde(deserialize_with = "deserialize_required_nullable")] + event_reference_request_id: RequiredNullable<String>, + #[serde(deserialize_with = "deserialize_required_nullable")] + address_reference_request_id: RequiredNullable<String>, + #[serde(deserialize_with = "deserialize_required_nullable")] + address_reference_cutoff: RequiredNullable<i64>, +} + +#[derive(serde::Deserialize)] +#[serde(transparent)] +struct RequiredNullable<T>(Option<T>); + +fn deserialize_required_nullable<'de, D, T>( + deserializer: D, +) -> Result<RequiredNullable<T>, D::Error> +where + D: serde::Deserializer<'de>, + T: serde::Deserialize<'de>, +{ + <Option<T> as serde::Deserialize>::deserialize(deserializer).map(RequiredNullable) +} + +#[tokio::test] +async fn nip09_reconciliation_v1_result_vector() { + assert_eq!( + RESULT_VECTOR_EXECUTOR_ID, + nip09_manifest::NIP09_RECONCILIATION_RESULT_VECTOR_EXECUTOR_ID + ); + assert_eq!( + sha256_hex(NIP09_RESULT_VECTOR_BYTES), + nip09_manifest::NIP09_RECONCILIATION_RESULT_VECTOR_SHA256 + ); + let vector: Nip09ReconciliationVector = serde_json::from_slice(NIP09_RESULT_VECTOR_BYTES) + .expect("strict NIP-09 reconciliation vector"); + assert_eq!(vector.schema_version, 1); + assert_eq!(vector.hook_id, nip09_manifest::NIP09_RECONCILIATION_HOOK_ID); + assert!(!vector.cases.is_empty()); + + for case in vector.cases { + execute_case(case).await; + } +} + +fn sha256_hex(bytes: &[u8]) -> String { + hex::encode(Sha256::digest(bytes)) +} + +async fn execute_case(case: Nip09ReconciliationVectorCase) { + let pool = open_v1_store(case.id.as_str()).await; + { + let mut connection = pool + .acquire() + .await + .unwrap_or_else(|error| panic!("{}: acquire v1 store: {error}", case.id)); + for input in &case.input_events { + seed_v1_raw_event(&mut connection, case.id.as_str(), input).await; + } + } + + let generation: [u8; 32] = hex::decode(&case.source_generation_hex) + .unwrap_or_else(|error| panic!("{}: source generation hex: {error}", case.id)) + .try_into() + .unwrap_or_else(|bytes: Vec<u8>| { + panic!("{}: source generation length {}", case.id, bytes.len()) + }); + apply_v1_reconciliation(&pool, case.id.as_str(), generation).await; + assert_results(&pool, case.id.as_str(), generation, case.expected).await; +} + +async fn open_v1_store(case_id: &str) -> SqlitePool { + let options = SqliteConnectOptions::from_str("sqlite::memory:") + .unwrap_or_else(|error| panic!("{case_id}: memory options: {error}")) + .foreign_keys(true); + let pool = SqlitePoolOptions::new() + .max_connections(1) + .connect_with(options) + .await + .unwrap_or_else(|error| panic!("{case_id}: open v1 store: {error}")); + sqlx::raw_sql(NIP09_EVENT_STORE_V1_UP_SQL) + .execute(&pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: install v1 schema: {error}")); + pool +} + +async fn seed_v1_raw_event( + connection: &mut SqliteConnection, + case_id: &str, + input: &Nip09ReconciliationVectorInput, +) { + let wire = RadrootsNip01EventWire { + id: input.event.id.clone(), + pubkey: input.event.pubkey.clone(), + created_at: input.event.created_at, + kind: input.event.kind, + tags: input.event.tags.clone(), + content: input.event.content.clone(), + sig: input.event.sig.clone(), + extra: Default::default(), + }; + let raw_json = serde_json::to_string(&wire) + .unwrap_or_else(|error| panic!("{case_id}: event JSON: {error}")); + let ingest = + RadrootsEventIngest::from_raw_json_reconciliation_v1(raw_json, input.observed_at_ms) + .unwrap_or_else(|error| panic!("{case_id}: vector event verification: {error}")); + let event = ingest.event(); + let admission = EventAdmission::for_profile( + ReconciliationProfile::Nip09V1RegistryV7, + ingest.verified_event(), + ) + .unwrap_or_else(|error| panic!("{case_id}: registry-v7 admission: {error}")); + if input.event.kind == KIND_LIST_SET_FOLLOW { + assert_eq!( + admission.status, + RadrootsEventAdmissionStatus::Admitted, + "{case_id}: {:?}", + admission.code + ); + assert_eq!( + admission.contract.map(|contract| contract.id), + Some("radroots.list_set.follow.v1"), + "{case_id}" + ); + } + + let tags = event.tags_as_vec(); + let tags_json = serde_json::to_string(&tags) + .unwrap_or_else(|error| panic!("{case_id}: tags JSON: {error}")); + let event_class = StoredEventClass::from_event_kind_class(event.kind_class()); + let valid_stream_eligible = admission.valid_stream_eligible(event.kind_class()); + let inserted = sqlx::query( + "INSERT INTO event_envelopes(event_id, pubkey, created_at, kind, tags_json, content, sig, raw_json, verification_status, contract_status, contract_id, event_class, projection_eligible, inserted_at_ms, updated_at_ms) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'verified', ?, ?, ?, ?, ?, ?)", + ) + .bind(event.id_hex()) + .bind(event.author().to_hex()) + .bind( + i64::try_from(event.created_at_u64()) + .unwrap_or_else(|_| panic!("{case_id}: created_at exceeds SQLite range")), + ) + .bind(i64::from(event.kind_u32())) + .bind(tags_json) + .bind(event.content()) + .bind(event.signature_hex()) + .bind(ingest.raw_json()) + .bind(admission.status.as_str()) + .bind(admission.contract.map(|contract| contract.id)) + .bind(event_class.as_str()) + .bind(i64::from(valid_stream_eligible)) + .bind(input.observed_at_ms) + .bind(input.observed_at_ms) + .execute(&mut *connection) + .await + .unwrap_or_else(|error| panic!("{case_id}: seed raw event: {error}")); + assert_eq!(inserted.rows_affected(), 1, "{case_id}: raw event insert"); + + for (index, tag) in tags.iter().enumerate() { + let tag_name = tag.first().map(String::as_str).unwrap_or(""); + let tag_value = tag.get(1).map(String::as_str); + let tag_json = serde_json::to_string(tag) + .unwrap_or_else(|error| panic!("{case_id}: tag JSON: {error}")); + let contract_tag = admission.contract.and_then(|contract| { + contract + .tags + .iter() + .find(|candidate| candidate.name == tag_name) + }); + let inserted = sqlx::query( + "INSERT INTO event_envelope_tags(event_id, tag_index, tag_name, tag_value, tag_json, contract_semantic, contract_value_type, relay_indexed) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + ) + .bind(event.id_hex()) + .bind( + i64::try_from(index) + .unwrap_or_else(|_| panic!("{case_id}: tag index exceeds SQLite range")), + ) + .bind(tag_name) + .bind(tag_value) + .bind(tag_json) + .bind(contract_tag.map(|tag| tag_semantic_name(tag.semantic))) + .bind(contract_tag.map(|tag| tag_value_type_name(tag.value_type))) + .bind(i64::from( + contract_tag.map(|tag| tag.relay_indexed).unwrap_or(false), + )) + .execute(&mut *connection) + .await + .unwrap_or_else(|error| panic!("{case_id}: seed raw tag: {error}")); + assert_eq!(inserted.rows_affected(), 1, "{case_id}: raw tag insert"); + } +} + +async fn apply_v1_reconciliation(pool: &SqlitePool, case_id: &str, generation: [u8; 32]) { + let mut transaction = pool + .begin_with("BEGIN IMMEDIATE") + .await + .unwrap_or_else(|error| panic!("{case_id}: reconciliation transaction: {error}")); + sqlx::raw_sql(NIP09_V1_UP_SQL) + .execute(&mut *transaction) + .await + .unwrap_or_else(|error| panic!("{case_id}: install NIP-09 v1 schema: {error}")); + apply_reconciliation_hook( + &mut transaction, + &FixedResultVectorGeneration(generation), + ReconciliationCapacityLimits::production(), + ) + .await + .unwrap_or_else(|error| panic!("{case_id}: NIP-09 v1 reconciliation: {error}")); + validate_applied_hook_state(&mut transaction) + .await + .unwrap_or_else(|error| panic!("{case_id}: NIP-09 v1 deep validation: {error}")); + transaction + .commit() + .await + .unwrap_or_else(|error| panic!("{case_id}: reconciliation commit: {error}")); +} + +async fn assert_results( + pool: &SqlitePool, + case_id: &str, + generation: [u8; 32], + expected: Nip09ReconciliationVectorExpected, +) { + let stored_generation: Vec<u8> = sqlx::query_scalar( + "SELECT active_generation FROM radroots_event_store_source_state WHERE singleton = 1", + ) + .fetch_one(pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: source generation: {error}")); + assert_eq!(stored_generation.as_slice(), generation, "{case_id}"); + + let source_raw_event_count: i64 = sqlx::query_scalar( + "SELECT raw_event_count FROM radroots_event_store_source_state WHERE singleton = 1", + ) + .fetch_one(pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: source raw count: {error}")); + let raw_event_count = row_count(pool, case_id, "event_envelopes").await; + let coordinate_count = row_count(pool, case_id, "radroots_event_store_event_coordinate").await; + let request_count = row_count(pool, case_id, "radroots_event_store_nip09_request").await; + let event_target_count = + row_count(pool, case_id, "radroots_event_store_nip09_event_target").await; + let address_target_count = + row_count(pool, case_id, "radroots_event_store_nip09_address_target").await; + let transition_count = row_count( + pool, + case_id, + "radroots_event_store_addressable_head_transition", + ) + .await; + assert_eq!( + ( + source_raw_event_count, + raw_event_count, + coordinate_count, + request_count, + event_target_count, + address_target_count, + transition_count, + ), + ( + expected.raw_event_count, + expected.raw_event_count, + expected.coordinate_count, + expected.request_count, + expected.event_target_count, + expected.address_target_count, + expected.transition_count, + ), + "{case_id}" + ); + + let state = sqlx::query( + "SELECT kind, pubkey, d_tag, raw_head_event_id, admission_status, contract_id, visibility, nip09_outcome, nip09_reason, event_reference_request_id, address_reference_request_id, address_reference_cutoff FROM radroots_event_store_addressable_head_state", + ) + .fetch_one(pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: state: {error}")); + assert_eq!( + state.try_get::<i64, _>("kind").expect("kind"), + expected.state.kind, + "{case_id}" + ); + assert_eq!( + state.try_get::<String, _>("pubkey").expect("pubkey"), + expected.state.pubkey, + "{case_id}" + ); + assert_eq!( + state.try_get::<String, _>("d_tag").expect("d_tag"), + expected.state.d_tag, + "{case_id}" + ); + assert_eq!( + state + .try_get::<String, _>("raw_head_event_id") + .expect("raw_head_event_id"), + expected.state.raw_head_event_id, + "{case_id}" + ); + assert_eq!( + state + .try_get::<String, _>("admission_status") + .expect("admission_status"), + expected.state.admission_status, + "{case_id}" + ); + assert_eq!( + state + .try_get::<String, _>("contract_id") + .expect("contract_id"), + expected.state.contract_id, + "{case_id}" + ); + assert_eq!( + state + .try_get::<String, _>("visibility") + .expect("visibility"), + expected.state.visibility, + "{case_id}" + ); + assert_eq!( + state + .try_get::<String, _>("nip09_outcome") + .expect("nip09_outcome"), + expected.state.nip09_outcome, + "{case_id}" + ); + assert_eq!( + state + .try_get::<String, _>("nip09_reason") + .expect("nip09_reason"), + expected.state.nip09_reason, + "{case_id}" + ); + assert_eq!( + state + .try_get::<Option<String>, _>("event_reference_request_id") + .expect("event_reference_request_id"), + expected.state.event_reference_request_id.0, + "{case_id}" + ); + assert_eq!( + state + .try_get::<Option<String>, _>("address_reference_request_id") + .expect("address_reference_request_id"), + expected.state.address_reference_request_id.0, + "{case_id}" + ); + assert_eq!( + state + .try_get::<Option<i64>, _>("address_reference_cutoff") + .expect("address_reference_cutoff"), + expected.state.address_reference_cutoff.0, + "{case_id}" + ); + assert_contiguous_transition_sequence(pool, case_id).await; +} + +async fn row_count(pool: &SqlitePool, case_id: &str, table: &'static str) -> i64 { + let statement = format!("SELECT COUNT(*) FROM {table}"); + sqlx::query_scalar(sqlx::AssertSqlSafe(statement)) + .fetch_one(pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: {table} count: {error}")) +} + +async fn assert_contiguous_transition_sequence(pool: &SqlitePool, case_id: &str) { + let sequences = sqlx::query_scalar::<_, i64>( + "SELECT transition_seq FROM radroots_event_store_addressable_head_transition ORDER BY transition_seq", + ) + .fetch_all(pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: transition sequence: {error}")); + for (index, sequence) in sequences.iter().enumerate() { + assert_eq!( + *sequence, + i64::try_from(index + 1).expect("sequence"), + "{case_id}" + ); + } + let last_transition: i64 = sqlx::query_scalar( + "SELECT last_transition_seq FROM radroots_event_store_source_state WHERE singleton = 1", + ) + .fetch_one(pool) + .await + .unwrap_or_else(|error| panic!("{case_id}: last transition: {error}")); + assert_eq!( + last_transition, + sequences.last().copied().unwrap_or_default(), + "{case_id}" + ); +} + +#[test] +fn nip09_reconciliation_v1_result_vector_requires_complete_state() { + let vector: serde_json::Value = + serde_json::from_slice(NIP09_RESULT_VECTOR_BYTES).expect("result-vector JSON"); + + let mut missing_state = vector.clone(); + missing_state["cases"][0]["expected"] + .as_object_mut() + .expect("expected object") + .remove("state"); + assert!( + serde_json::from_value::<Nip09ReconciliationVector>(missing_state).is_err(), + "missing durable state must fail strict executor parsing" + ); + + for field in [ + "kind", + "pubkey", + "d_tag", + "raw_head_event_id", + "admission_status", + "contract_id", + "visibility", + "nip09_outcome", + "nip09_reason", + "event_reference_request_id", + "address_reference_request_id", + "address_reference_cutoff", + ] { + let mut missing_field = vector.clone(); + missing_field["cases"][0]["expected"]["state"] + .as_object_mut() + .expect("state object") + .remove(field); + assert!( + serde_json::from_value::<Nip09ReconciliationVector>(missing_field).is_err(), + "missing durable state field `{field}` must fail strict executor parsing" + ); + } +} diff --git a/crates/test_fixtures/Cargo.toml b/crates/test_fixtures/Cargo.toml @@ -21,3 +21,4 @@ radroots_event = { workspace = true, features = [ "knowledge", "knowledge-nip54", ] } +radroots_identity = { workspace = true } diff --git a/crates/test_fixtures/src/knowledge.rs b/crates/test_fixtures/src/knowledge.rs @@ -19,6 +19,7 @@ use radroots_event::knowledge::{ RadrootsKnowledgeReviewTarget, RadrootsKnowledgeSource, RadrootsWikiArticle, RadrootsWikiArticleVersionRef, RadrootsWikiMergeRequest, RadrootsWikiRedirect, }; +use radroots_identity::PublicKey; use crate::RELAY_PRIMARY_WSS; @@ -143,7 +144,10 @@ pub fn hex_64(character: char) -> String { pub fn event_ref(character: char, kind: u32) -> RadrootsEventRef { RadrootsEventRef { id: hex_64(character), - author: hex_64('a'), + author: PublicKey::from_hex( + "585591529da0bab31b3b1f986611cf5f435dca84f978c89ee8a40cca7103df", + ) + .expect("fixture public key is valid"), kind, d_tag: None, relays: Some(vec![RELAY_PRIMARY_WSS.to_string()]), diff --git a/tools/xtask/src/contract/food_availability_projection.rs b/tools/xtask/src/contract/food_availability_projection.rs @@ -3905,6 +3905,30 @@ mod tests { fs::read_to_string(&executor_path).expect("read successor FoodAvailability executor"); let predecessor_executor = successor_executor .replace( + ".map(|projection| projection.event_id().to_hex())", + ".map(|projection| projection.event_id().as_str())", + ) + .replace( + "actual.raw_head_event_id().map(|event_id| event_id.to_hex()),\n expected.raw_head_event_id.0.clone(),", + "actual.raw_head_event_id().map(|event_id| event_id.as_str()),\n expected.raw_head_event_id.0.as_deref(),", + ) + .replace( + "historical_payload.event_id().to_hex()", + "historical_payload.event_id().as_str()", + ) + .replace( + "actual.event_id().to_hex()", + "actual.event_id().as_str()", + ) + .replace( + ".event_reference_request_id()\n .map(|event_id| event_id.to_hex()),\n expected.event_reference_request_id.0.clone(),", + ".event_reference_request_id()\n .map(|event_id| event_id.as_str()),\n expected.event_reference_request_id.0.as_deref(),", + ) + .replace( + ".address_reference_request_id()\n .map(|event_id| event_id.to_hex()),\n expected.address_reference_request_id.0.clone(),", + ".address_reference_request_id()\n .map(|event_id| event_id.as_str()),\n expected.address_reference_request_id.0.as_deref(),", + ) + .replace( "use radroots_blossom::Sha256;", "use radroots_blossom::RadrootsBlossomSha256;", ) diff --git a/tools/xtask/src/contract/nip09_reconciliation.rs b/tools/xtask/src/contract/nip09_reconciliation.rs @@ -91,7 +91,7 @@ const EVENT_STORE_STORE_ROOT_BASELINE_SHA256: &str = const EVENT_STORE_MIGRATION_IMPL_BASELINE_SHA256: &str = "69f3c730f8a4f3a4af0028c74f6903126def01ceb63eed8a173e696ce291dc09"; const EVENT_CRATE_ROOT_BASELINE_SHA256: &str = - "ec032e602c1afaebc5a998e74cd1e70066afd9a07b82bce2d0d7307caac15cd6"; + "dda56a8bdb3f45f19e401569ece182d1a3ad6118488f2ac591a16d2af468b5bc"; const EVENT_CODEC_CRATE_ROOT_BASELINE_SHA256: &str = "8c29ceccb06abc94279db3257d52e14ad721e2eb302e54fbf26f8856d0db0b53"; const BLOSSOM_CRATE_ROOT_BASELINE_SHA256: &str = @@ -9847,9 +9847,9 @@ fn validate_post_core_storage_method_signature( "quarantine_trade" => Some( "async fn quarantine_trade( &mut self, - trade_id: Option<&str>, - mutation_id: Option<&str>, - transport_event_id: Option<&str>, + trade_id: Option<String>, + mutation_id: Option<String>, + transport_event_id: Option<String>, reason: &str, observed_at_ms: i64, ) -> Result<(), RadrootsEventStoreError>", diff --git a/tools/xtask/src/contract/source_maintenance.rs b/tools/xtask/src/contract/source_maintenance.rs @@ -72,6 +72,10 @@ const RESULT_VECTOR_EXECUTOR_ID: &str = const RESULT_VECTOR_EXECUTOR_TEST: &str = "source_maintenance_v1_result_vector"; const FOOD_PREDECESSOR_RESULT_VECTOR_EXECUTOR_RELATIVE: &str = "crates/event_store/tests/food_availability_projection_v1_result_vector.rs"; +const NIP09_SUCCESSOR_RESULT_VECTOR_EXECUTOR_RELATIVE: &str = + "crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs"; +const NIP09_SUCCESSOR_RESULT_VECTOR_EXECUTOR_ID: &str = + "radroots_event_store.nip09_reconciliation_v1.result_vector_executor.v2"; const CONTRACT_COMMAND_SOURCE_RELATIVE: &str = "tools/xtask/src/contract.rs"; const XTASK_MAIN_SOURCE_RELATIVE: &str = "tools/xtask/src/main.rs"; const XTASK_MAIN_FULL_AST_SHA256: &str = @@ -498,6 +502,10 @@ const SOURCE_SPECS: &[SourceSpec] = &[ path: "crates/event_store/src/nip09/reconciliation_v1.rs", }, SourceSpec { + role: "nip09_successor_result_vector_executor", + path: NIP09_SUCCESSOR_RESULT_VECTOR_EXECUTOR_RELATIVE, + }, + SourceSpec { role: "schema_migration_and_reopen_authority", path: "crates/event_store/src/schema.rs", }, @@ -1308,6 +1316,7 @@ pub(super) fn validate_source_contract(workspace_root: &Path) -> Result<(), Stri validate_ingest_capacity_authority(workspace_root)?; validate_schema_capacity_authority(workspace_root)?; validate_generation_rebuild_authority(workspace_root)?; + validate_nip09_successor_result_vector_executor(workspace_root)?; validate_sql_capacity_authority(workspace_root)?; validate_contract_command_reachability_authority(workspace_root)?; validate_current_event_store_successor_authority(workspace_root) @@ -2036,6 +2045,50 @@ fn validate_generation_rebuild_authority(workspace_root: &Path) -> Result<(), St ) } +fn validate_nip09_successor_result_vector_executor(workspace_root: &Path) -> Result<(), String> { + let module_relative = "crates/event_store/src/nip09/reconciliation_v1.rs"; + let module = compact_rust( + &rust_source(workspace_root, module_relative)?, + module_relative, + )?; + require_ordered_markers( + "SourceMaintenance NIP-09 successor module selection", + &module, + &[ + "#[path=\"../../tests/support/nip09_reconciliation_v1_result_vector_v2.rs\"]modresult_vector_executor;", + "include_bytes!(\"../../tests/fixtures/nip09_reconciliation.v1.json\")", + "include_str!(\"../../migrations/0001_event_store.up.sql\")", + "include_str!(\"../../migrations/0002_nip09.up.sql\")", + "implSourceGenerationProviderforFixedResultVectorGeneration", + ], + )?; + + let relative = NIP09_SUCCESSOR_RESULT_VECTOR_EXECUTOR_RELATIVE; + let source = rust_source(workspace_root, relative)?; + let compact = compact_rust(&source, relative)?; + require_ordered_markers( + "SourceMaintenance NIP-09 successor result-vector executor", + &compact, + &[ + &format!( + "constRESULT_VECTOR_EXECUTOR_ID:&str=\"{NIP09_SUCCESSOR_RESULT_VECTOR_EXECUTOR_ID}\"" + ), + "sha256_hex(NIP09_RESULT_VECTOR_BYTES)", + "sqlx::raw_sql(NIP09_EVENT_STORE_V1_UP_SQL)", + ".bind(event.id_hex())", + ".bind(event.signature_hex())", + ".bind(event.id_hex())", + "sqlx::raw_sql(NIP09_V1_UP_SQL)", + ], + )?; + if compact.contains(".id_str()") || compact.contains(".sig_str()") { + return Err(format!( + "{relative} must use explicit canonical hexadecimal boundary encoders" + )); + } + Ok(()) +} + fn validate_sql_capacity_authority(workspace_root: &Path) -> Result<(), String> { let sql = read_regular_file(workspace_root, MIGRATION_UP_RELATIVE)?; let sql = std::str::from_utf8(&sql) @@ -3523,7 +3576,7 @@ struct DelegatedAuthoritySpec { const EXECUTABLE_AUTHORITY_AST_SHA256: &str = "da4ba88c3cabe0e5ed6df4ca0115645c8a65d43cdea7d2dbf5e78d99256e8e15"; const BOUND_AUTHORITY_SOURCE_AST_SHA256: &str = - "14d31381f73b684c3480531acb5321c66ef04b7cb8e25aa6a06269fb4ee6601f"; + "bd1d92cdd1e399a844f5957ef2117d6860bc309b66af24b56ad251a464b2a6dd"; #[derive(Clone, Debug, Serialize)] struct ExecutableAuthorityIdentity {