upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/grasp-audit/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-20 23:44:05 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-21 03:05:41 +0000
commit2bbb7292c978d36464b6166faa78223677389ef6 (patch)
treeef82eb6f9267c27a08e758d32112a33c13991717 /grasp-audit/src
parent519fdc66930280cd1772417dca327ed858333d64 (diff)
Implement GRASP-01 stateful write policy with database queries
- Add Nip34WritePolicy with Arc<MemoryDatabase> for stateful event validation - Implement full GRASP-01 event acceptance policy: * Accept events referencing accepted repositories (via a, A, q tags) * Accept events referencing accepted events (transitive, via e, E, q tags) * Support forward references (events referenced by accepted events) * Reject orphan events with no valid references - Extract and validate all reference tag types (a, A, q, e, E) - Query database for repository and event existence checks - Implement fail-secure error handling for database query failures Test improvements: - Fix send_and_verify_rejected to handle relay rejection errors properly - Fix RepoWithIssue fixture usage in forward reference tests - Add database synchronization polling for race condition mitigation - Achieve 94% test pass rate (16/17 integration tests passing)
Diffstat (limited to 'grasp-audit/src')
-rw-r--r--grasp-audit/src/specs/grasp01/event_acceptance_policy.rs76
1 files changed, 52 insertions, 24 deletions
diff --git a/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs b/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs
index 638ae5f..c1977f9 100644
--- a/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs
+++ b/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs
@@ -551,11 +551,36 @@ impl EventAcceptancePolicyTests {
551 ) -> Result<(), String> { 551 ) -> Result<(), String> {
552 let event_id = event.id; 552 let event_id = event.id;
553 553
554 client 554 // Try to send event - rejection may cause send_event to fail with an error
555 .send_event(event) 555 let send_result = client.send_event(event).await;
556 .await 556
557 .map_err(|e| format!("Failed to send event to relay: {}", e))?; 557 // If send succeeded, the relay might have accepted it (we'll verify below)
558 // If send failed, check if it's a rejection error (expected)
559 if let Err(e) = send_result {
560 let err_msg = e.to_string().to_lowercase();
561 // Check if error message indicates rejection (not network/other errors)
562 if err_msg.contains("rejected") || err_msg.contains("blocked") {
563 // Expected rejection - verify event is NOT in database
564 tokio::time::sleep(Duration::from_millis(100)).await;
565
566 let filter = Filter::new().id(event_id);
567 let events = client
568 .query(filter)
569 .await
570 .map_err(|e| format!("Failed to query relay for verification: {}", e))?;
571
572 if !events.is_empty() {
573 return Err(format!("Event was rejected but still stored: {}", description));
574 }
575
576 return Ok(()); // Rejected as expected
577 } else {
578 // Unexpected error (network, etc.)
579 return Err(format!("Failed to send event to relay: {}", e));
580 }
581 }
558 582
583 // Send succeeded, verify event was NOT stored (relay should have rejected)
559 tokio::time::sleep(Duration::from_millis(100)).await; 584 tokio::time::sleep(Duration::from_millis(100)).await;
560 585
561 let filter = Filter::new().id(event_id); 586 let filter = Filter::new().id(event_id);
@@ -877,6 +902,26 @@ impl EventAcceptancePolicyTests {
877 ) 902 )
878 })?; 903 })?;
879 904
905 // Verify repo is queryable (ensures it's fully indexed before we reference it)
906 let repo_id = Self::extract_d_tag(&repo).ok_or("Failed to extract repo_id")?;
907 let verify_filter = Filter::new()
908 .kind(Kind::GitRepoAnnouncement)
909 .author(repo.pubkey)
910 .identifier(repo_id);
911
912 // Poll until repo is available (with timeout)
913 for _ in 0..10 {
914 let events = client.query(verify_filter.clone()).await
915 .map_err(|e| format!("Failed to verify repo: {}", e))?;
916 if !events.is_empty() {
917 break;
918 }
919 tokio::time::sleep(Duration::from_millis(50)).await;
920 }
921
922 // Extra delay to ensure relay's internal database is fully synchronized
923 tokio::time::sleep(Duration::from_millis(200)).await;
924
880 // Create Kind 1 note locally but DON'T send it yet 925 // Create Kind 1 note locally but DON'T send it yet
881 let kind1_note = client 926 let kind1_note = client
882 .event_builder(Kind::TextNote, "Note to be referenced") 927 .event_builder(Kind::TextNote, "Note to be referenced")
@@ -938,34 +983,17 @@ impl EventAcceptancePolicyTests {
938 // Create TestContext 983 // Create TestContext
939 let ctx = TestContext::new(client); 984 let ctx = TestContext::new(client);
940 985
941 // Get repo with issue fixture (mode-aware) 986 // Get issue fixture (mode-aware) - RepoWithIssue returns the issue event directly
942 let repo = ctx 987 let issue = ctx
943 .get_fixture(FixtureKind::RepoWithIssue) 988 .get_fixture(FixtureKind::RepoWithIssue)
944 .await 989 .await
945 .map_err(|e| { 990 .map_err(|e| {
946 format!( 991 format!(
947 "Test setup failed: could not get repo with issue fixture: {}", 992 "Test setup failed: could not get issue fixture: {}",
948 e 993 e
949 ) 994 )
950 })?; 995 })?;
951 996
952 // Extract the issue from the repo event (it's stored as the first 'e' tag)
953 let issue_id = repo
954 .tags
955 .iter()
956 .find(|t| t.kind() == TagKind::e())
957 .and_then(|t| t.content())
958 .ok_or("Missing issue reference in RepoWithIssue fixture")?;
959
960 // Query to get the actual issue event
961 let filter = Filter::new().id(nostr_sdk::EventId::from_hex(issue_id)
962 .map_err(|e| format!("Invalid issue ID: {}", e))?);
963 let issues = client
964 .query(filter)
965 .await
966 .map_err(|e| format!("Failed to query issue: {}", e))?;
967 let issue = issues.first().ok_or("Issue not found")?.clone();
968
969 // Create Comment A locally but DON'T send it yet 997 // Create Comment A locally but DON'T send it yet
970 let comment_a = Self::create_comment_for_event(client, &issue, "Comment A")?; 998 let comment_a = Self::create_comment_for_event(client, &issue, "Comment A")?;
971 999