Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions event-svc/src/event/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,26 +544,42 @@ impl EventService {
}

/// Get the chain proof for a given event from the database.
/// All proofs should have been validated and stored during the event validation phase (v0.55.0+).
/// All proofs should have been validated and stored during the event validation phase (v0.55.0+),
/// but in case it can't be found we try to dynamically discover it through the RPC provider
pub(crate) async fn discover_chain_proof(
&self,
event: &ceramic_event::unvalidated::TimeEvent,
) -> std::result::Result<ChainProof, crate::eth_rpc::Error> {
let tx_hash = event.proof().tx_hash();
let tx_hash = tx_hash_try_from_cid(tx_hash).unwrap().to_string();
self.event_access
if let Some(proof) = self
.event_access
.get_chain_proof(event.proof().chain_id(), &tx_hash)
.await
.map_err(|e| crate::eth_rpc::Error::Application(e.into()))?
.ok_or_else(|| {
// Note: Using InvalidProof here rather than TxNotFound because:
// - TxNotFound is for "transaction not found on blockchain" (RPC-level)
// - This is "proof not in local database" (local storage issue)
crate::eth_rpc::Error::InvalidProof(format!(
"Chain proof for tx {} not found in database.",
tx_hash
))
})
{
return Ok(proof);
}

warn!(
"Chain proof for tx {} not found in database, validating and storing it now.",
tx_hash
);

// Try using the RPC provider and store the proof
let proof = self
.event_validator
.time_event_validator()
.validate_chain_inclusion(event)
.await?;

let proof = ChainProof::from(proof);
self.event_access
.persist_chain_inclusion_proofs(std::slice::from_ref(&proof))
.await
.map_err(|e| crate::eth_rpc::Error::Application(e.into()))?;

Ok(proof)
}
}

Expand Down
4 changes: 4 additions & 0 deletions event-svc/src/event/validator/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ impl EventValidator {
}
}
}

pub fn time_event_validator(&self) -> &TimeEventValidator {
&self.time_event_validator
}
}

#[cfg(test)]
Expand Down
31 changes: 12 additions & 19 deletions event-svc/src/tests/self_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,21 +614,23 @@ async fn test_discover_chain_proof_direct() {
assert_eq!(proof.timestamp, timestamp as i64);
}

/// Test that discover_chain_proof returns an error for non-existent proofs.
/// Test that discover_chain_proof falls back to RPC when proof not in DB,
/// and returns NoChainProvider error when no RPC provider is configured.
#[tokio::test]
async fn test_discover_chain_proof_not_found() {
async fn test_discover_chain_proof_fallback_no_provider() {
use ceramic_event::unvalidated;

use crate::{EventService, UndeliveredEventReview};

let pool = SqlitePool::connect_in_memory().await.unwrap();
// Service created with no RPC providers
let service = EventService::try_new(pool.clone(), UndeliveredEventReview::Skip, false, vec![])
.await
.unwrap();

// Create a time event for a non-existent chain proof
let tx_hash = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
let tx_hash_cid = tx_hash_to_cid(tx_hash);
let tx_hash_cid =
tx_hash_to_cid("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
let init_cid = super::deterministic_cid(b"discover not found test init");
let prev_cid = super::deterministic_cid(b"discover not found test prev");

Expand All @@ -643,29 +645,20 @@ async fn test_discover_chain_proof_not_found() {
.build()
.expect("test time event should build");

// Call discover_chain_proof - should fail because no proof was stored
// Call discover_chain_proof - should fail because proof not in DB and no RPC provider
let result = service.discover_chain_proof(&time_event).await;

assert!(
result.is_err(),
"discover_chain_proof should fail for non-existent proof"
"discover_chain_proof should fail when no RPC provider configured"
);

// Verify it's an InvalidProof error (discover_chain_proof returns InvalidProof when not found)
// Verify it's a NoChainProvider error (RPC fallback fails without provider)
match result.unwrap_err() {
crate::eth_rpc::Error::InvalidProof(msg) => {
assert!(
msg.contains("not found in database"),
"Error message should indicate proof not found, got: {}",
msg
);
assert!(
msg.contains(tx_hash),
"Error message should contain tx_hash, got: {}",
msg
);
crate::eth_rpc::Error::NoChainProvider(chain_id) => {
assert_eq!(chain_id.to_string(), "eip155:100");
}
other => panic!("Expected InvalidProof error, got: {:?}", other),
other => panic!("Expected NoChainProvider error, got: {:?}", other),
}
}

Expand Down
Loading