Skip to content

Commit

Permalink
refactor: make ChainInclusion trait fully generic and change input to…
Browse files Browse the repository at this point in the history
… the raw AnchorProof
  • Loading branch information
stbrody committed Oct 22, 2024
1 parent 7c7dc40 commit 474bfd3
Show file tree
Hide file tree
Showing 17 changed files with 79 additions and 121 deletions.
6 changes: 3 additions & 3 deletions anchor-remote/src/cas_remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use ceramic_anchor_service::{
};
use ceramic_car::CarReader;
use ceramic_core::{Cid, NodeId, StreamId};
use ceramic_event::unvalidated::Proof;
use ceramic_event::unvalidated::AnchorProof;

pub const AGENT_VERSION: &str = concat!("ceramic-one/", env!("CARGO_PKG_VERSION"));

Expand Down Expand Up @@ -200,11 +200,11 @@ async fn parse_anchor_response(anchor_response: String) -> Result<CasResponsePar
let mut car_reader = CarReader::new(witness_car_bytes.as_ref()).await?;
let mut remote_merkle_nodes = MerkleNodes::default();
let mut detached_time_event: Option<DetachedTimeEvent> = None;
let mut proof: Option<Proof> = None;
let mut proof: Option<AnchorProof> = None;
while let Some((cid, block)) = car_reader.next_block().await? {
if let Ok(block) = serde_ipld_dagcbor::from_slice::<DetachedTimeEvent>(&block) {
detached_time_event = Some(block);
} else if let Ok(block) = serde_ipld_dagcbor::from_slice::<Proof>(&block) {
} else if let Ok(block) = serde_ipld_dagcbor::from_slice::<AnchorProof>(&block) {
proof = Some(block);
} else if let Ok(block) = serde_ipld_dagcbor::from_slice::<MerkleNode>(&block) {
remote_merkle_nodes.insert(cid, block);
Expand Down
6 changes: 3 additions & 3 deletions anchor-service/src/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt::Debug;
use tracing::info;

use ceramic_core::{EventId, SerializeExt};
use ceramic_event::unvalidated::{Proof, ProofEdge, RawTimeEvent, TimeEvent};
use ceramic_event::unvalidated::{AnchorProof, ProofEdge, RawTimeEvent, TimeEvent};

/// AnchorRequest for a Data Event on a Stream
#[derive(Clone, PartialEq, Eq, Serialize)]
Expand Down Expand Up @@ -98,7 +98,7 @@ pub struct TimeEventBatch {
/// The intermediate Merkle Tree Nodes
pub merkle_nodes: MerkleNodes,
/// The anchor proof
pub proof: Proof,
pub proof: AnchorProof,
/// The Time Events
pub raw_time_events: RawTimeEvents,
}
Expand Down Expand Up @@ -138,7 +138,7 @@ impl TimeEventBatch {

/// Build a TimeEventInsertable from a RawTimeEvent and AnchorRequest
fn build_time_event_insertable(
proof: &Proof,
proof: &AnchorProof,
merkle_nodes: &MerkleNodes,
time_event: RawTimeEvent,
anchor_request: AnchorRequest,
Expand Down
4 changes: 2 additions & 2 deletions anchor-service/src/cas_mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use async_trait::async_trait;
use multihash_codetable::{Code, MultihashDigest};

use ceramic_core::{Cid, EventId, NodeId, SerializeExt};
use ceramic_event::unvalidated::Proof;
use ceramic_event::unvalidated::AnchorProof;

use crate::{
AnchorRequest, DetachedTimeEvent, RootTimeEvent, Store, TimeEventInsertable, TransactionManager,
Expand All @@ -17,7 +17,7 @@ pub struct MockCas;
#[async_trait]
impl TransactionManager for MockCas {
async fn anchor_root(&self, root_cid: Cid) -> Result<RootTimeEvent> {
let mock_proof = Proof::new(
let mock_proof = AnchorProof::new(
"mock chain id".to_string(),
root_cid,
root_cid,
Expand Down
4 changes: 2 additions & 2 deletions anchor-service/src/transaction_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use async_trait::async_trait;
use serde::{Deserialize, Serialize};

use ceramic_core::Cid;
use ceramic_event::unvalidated::Proof;
use ceramic_event::unvalidated::AnchorProof;

use crate::anchor::MerkleNodes;

/// A struct containing a blockchain proof CID, the path prefix to the CID in the anchored Merkle tree and the
/// corresponding Merkle tree nodes.
pub struct RootTimeEvent {
/// the proof data from the remote anchoring service
pub proof: Proof,
pub proof: AnchorProof,
/// the path through the remote Merkle tree
pub detached_time_event: DetachedTimeEvent,
/// the Merkle tree nodes from the remote anchoring service
Expand Down
2 changes: 1 addition & 1 deletion event-svc/src/event/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ impl<'a, S: BlockStore> Migrator<'a, S> {
.await
.context("finding proof block")
.with_model_context(&model)?;
let proof: unvalidated::Proof = serde_ipld_dagcbor::from_slice(&data)
let proof: unvalidated::AnchorProof = serde_ipld_dagcbor::from_slice(&data)
.context("decoding proof block")
.with_model_context(&model)?;
let mut curr = proof.root();
Expand Down
2 changes: 1 addition & 1 deletion event-svc/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ mod store;
mod validator;

pub use service::{BlockStore, DeliverableRequirement, EventService};
pub use validator::EthRpcProvider;
pub use validator::ChainInclusionProvider;
4 changes: 2 additions & 2 deletions event-svc/src/event/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use itertools::Itertools;
use recon::ReconItem;
use tracing::{trace, warn};

use crate::event::validator::EthRpcProvider;
use crate::event::validator::ChainInclusionProvider;
use crate::store::{EventAccess, EventInsertable, EventRowDelivered};
use crate::{Error, Result};

Expand Down Expand Up @@ -89,7 +89,7 @@ impl EventService {
pool: SqlitePool,
process_undelivered_events: bool,
validate_events: bool,
ethereum_rpc_providers: Vec<EthRpcProvider>,
ethereum_rpc_providers: Vec<ChainInclusionProvider>,
) -> Result<Self> {
let event_access = Arc::new(EventAccess::try_new(pool.clone()).await?);

Expand Down
6 changes: 3 additions & 3 deletions event-svc/src/event/validator/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ipld_core::ipld::Ipld;
use recon::ReconItem;
use tokio::try_join;

use crate::event::validator::EthRpcProvider;
use crate::event::validator::ChainInclusionProvider;
use crate::store::EventAccess;
use crate::{
event::{
Expand Down Expand Up @@ -131,7 +131,7 @@ impl EventValidator {
/// Create a new event validator
pub async fn try_new(
event_access: Arc<EventAccess>,
ethereum_rpc_providers: Vec<EthRpcProvider>,
ethereum_rpc_providers: Vec<ChainInclusionProvider>,
) -> Result<Self> {
let time_event_verifier = TimeEventValidator::new_with_providers(ethereum_rpc_providers);

Expand Down Expand Up @@ -226,7 +226,7 @@ impl EventValidator {
Ok(validated_events)
}

/// Transforms the [`ChainInclusionError`] into a [`ValidationError`] with an appropriate message
/// Transforms the [`eth_rpc::Error`] into a [`ValidationError`] with an appropriate message
fn convert_inclusion_error(err: eth_rpc::Error, order_key: &EventId) -> ValidationError {
match err {
eth_rpc::Error::TxNotFound { chain_id, tx_hash } => {
Expand Down
2 changes: 1 addition & 1 deletion event-svc/src/event/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ mod signed;

mod time;

pub use time::EthRpcProvider;
pub use time::ChainInclusionProvider;

pub use event::{EventValidator, UnvalidatedEvent, ValidatedEvent, ValidatedEvents};
70 changes: 23 additions & 47 deletions event-svc/src/event/validator/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ use ceramic_core::ssi::caip2;
use ceramic_event::unvalidated;
use tracing::warn;

use ceramic_validation::eth_rpc::{
self, ChainInclusion, EthProofType, EthTxProofInput, HttpEthRpc,
};
use ceramic_validation::eth_rpc::{self, ChainInclusion, HttpEthRpc};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Timestamp(u64);
Expand All @@ -20,13 +18,13 @@ impl Timestamp {
}
}

/// Provider for a remote Ethereum RPC endpoint.
pub type EthRpcProvider = Arc<dyn ChainInclusion<InclusionInput = EthTxProofInput> + Send + Sync>;
/// Provider for validating chain inclusion of an AnchorProof on a remote blockchain.
pub type ChainInclusionProvider = Arc<dyn ChainInclusion + Send + Sync>;

pub struct TimeEventValidator {
/// we could support multiple providers for each chain (to get around rate limits)
/// but we'll just force people to run a light client if they really need the throughput
chain_providers: HashMap<caip2::ChainId, EthRpcProvider>,
chain_providers: HashMap<caip2::ChainId, ChainInclusionProvider>,
}

impl std::fmt::Debug for TimeEventValidator {
Expand All @@ -50,7 +48,7 @@ impl TimeEventValidator {
Ok(provider) => {
// use the first valid rpc client we find rather than replace one
// could support an array of clients for a chain if desired
let provider: EthRpcProvider = Arc::new(provider);
let provider: ChainInclusionProvider = Arc::new(provider);
chain_providers
.entry(provider.chain_id().to_owned())
.or_insert_with(|| provider);
Expand All @@ -68,7 +66,7 @@ impl TimeEventValidator {

/// Create from known providers (e.g. inject mocks)
/// Currently used in tests, may switch to this from service if we want to share RPC with anchoring.
pub fn new_with_providers(providers: Vec<EthRpcProvider>) -> Self {
pub fn new_with_providers(providers: Vec<ChainInclusionProvider>) -> Self {
Self {
chain_providers: HashMap::from_iter(
providers.into_iter().map(|p| (p.chain_id().to_owned(), p)),
Expand All @@ -94,21 +92,19 @@ impl TimeEventValidator {
.get(&chain_id)
.ok_or_else(|| eth_rpc::Error::NoChainProvider(chain_id.clone()))?;

let input = EthTxProofInput {
tx_hash: event.proof().tx_hash(),
tx_type: EthProofType::from_str(event.proof().tx_type())
.map_err(|e| eth_rpc::Error::InvalidProof(e.to_string()))?,
};
let proof = provider.chain_inclusion_proof(&input).await?;
let chain_proof = provider.get_chain_inclusion_proof(event.proof()).await?;

if proof.root_cid != event.proof().root() {
// Compare the root CID in the TimeEvent's AnchorProof to the root CID that was actually
// included in the transaction onchain.
if chain_proof.root_cid != event.proof().root() {
return Err(eth_rpc::Error::InvalidProof(format!(
"the root CID is not in the transaction (root={})",
event.proof().root()
"the root CID is not in the transaction (anchor proof root={}, blockchain transaction root={})",
event.proof().root(),
chain_proof.root_cid,
)));
}

Ok(Timestamp(proof.timestamp))
Ok(Timestamp(chain_proof.timestamp))
}
}

Expand Down Expand Up @@ -248,15 +244,14 @@ mod test {
pub EthRpcProviderTest {}
#[async_trait::async_trait]
impl ChainInclusion for EthRpcProviderTest {
type InclusionInput = EthTxProofInput;

fn chain_id(&self) -> &caip2::ChainId;
async fn chain_inclusion_proof(&self, input: &EthTxProofInput) -> Result<eth_rpc::TimeProof, eth_rpc::Error>;
async fn get_chain_inclusion_proof(&self, input: &unvalidated::AnchorProof) -> Result<eth_rpc::ChainInclusionProof, eth_rpc::Error>;
}
}

async fn get_mock_provider(
input: eth_rpc::EthTxProofInput,
input: unvalidated::AnchorProof,
root_cid: Cid,
) -> TimeEventValidator {
let mut mock_provider = MockEthRpcProviderTest::new();
Expand All @@ -265,11 +260,11 @@ mod test {

mock_provider.expect_chain_id().once().return_const(chain);
mock_provider
.expect_chain_inclusion_proof()
.expect_get_chain_inclusion_proof()
.once()
.with(predicate::eq(input))
.return_once(move |_| {
Ok(eth_rpc::TimeProof {
Ok(eth_rpc::ChainInclusionProof {
timestamp: BLOCK_TIMESTAMP,
root_cid,
})
Expand All @@ -280,12 +275,8 @@ mod test {
#[test(tokio::test)]
async fn valid_proof_single() {
let event = time_event_single_event_batch();
let input = EthTxProofInput {
tx_hash: event.proof().tx_hash(),
tx_type: event.proof().tx_type().parse().unwrap(),
};
let verifier = get_mock_provider(event.proof().clone(), event.proof().root()).await;

let verifier = get_mock_provider(input, event.proof().root()).await;
match verifier.validate_chain_inclusion(&event).await {
Ok(ts) => {
assert_eq!(ts.as_unix_ts(), BLOCK_TIMESTAMP);
Expand All @@ -297,14 +288,10 @@ mod test {
#[test(tokio::test)]
async fn invalid_proof_single() {
let event = time_event_single_event_batch();
let input = EthTxProofInput {
tx_hash: event.proof().tx_hash(),
tx_type: event.proof().tx_type().parse().unwrap(),
};

let random_root =
Cid::from_str("bagcqceraxr7s7s32wsashm6mm4fonhpkvfdky4rvw6sntlu2pxtl3fjhj2aa").unwrap();
let verifier = get_mock_provider(input, random_root).await;
let verifier = get_mock_provider(event.proof().clone(), random_root).await;
match verifier.validate_chain_inclusion(&event).await {
Ok(v) => {
panic!("should have failed: {:?}", v)
Expand All @@ -323,13 +310,7 @@ mod test {
#[test(tokio::test)]
async fn valid_proof_multi() {
let event = time_event_multi_event_batch();

let input = EthTxProofInput {
tx_hash: event.proof().tx_hash(),
tx_type: event.proof().tx_type().parse().unwrap(),
};

let verifier = get_mock_provider(input, event.proof().root()).await;
let verifier = get_mock_provider(event.proof().clone(), event.proof().root()).await;

match verifier.validate_chain_inclusion(&event).await {
Ok(ts) => {
Expand All @@ -342,15 +323,10 @@ mod test {
#[test(tokio::test)]
async fn invalid_root_tx_proof_cid_multi() {
let event = time_event_multi_event_batch();

let input = EthTxProofInput {
tx_hash: event.proof().tx_hash(),
tx_type: event.proof().tx_type().parse().unwrap(),
};

let random_root =
Cid::from_str("bagcqceraxr7s7s32wsashm6mm4fonhpkvfdky4rvw6sntlu2pxtl3fjhj2aa").unwrap();
let verifier = get_mock_provider(input, random_root).await;
let verifier = get_mock_provider(event.proof().clone(), random_root).await;

match verifier.validate_chain_inclusion(&event).await {
Ok(v) => {
panic!("should have failed: {:?}", v)
Expand Down
2 changes: 1 addition & 1 deletion event-svc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod tests;

pub use ceramic_validation::eth_rpc;
pub use error::Error;
pub use event::EthRpcProvider;
pub use event::ChainInclusionProvider;
pub use event::{BlockStore, EventService};

pub(crate) type Result<T> = std::result::Result<T, Error>;
4 changes: 2 additions & 2 deletions event/src/unvalidated/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ impl TimeBuilderState for TimeBuilderWithPrev {}
impl TimeBuilder<TimeBuilderWithPrev> {
/// Build the [`unvalidated::TimeEvent`].
pub fn build(self) -> anyhow::Result<unvalidated::TimeEvent> {
let proof = unvalidated::Proof::new(
let proof = unvalidated::AnchorProof::new(
self.state.chain_id,
self.state.prev,
self.state.tx_hash,
Expand Down Expand Up @@ -381,7 +381,7 @@ impl TimeBuilder<TimeBuilderWithRoot> {
Ipld::Link(prev) => *prev,
_ => bail!("leaf indexed value should always be a Cid"),
};
let proof = unvalidated::Proof::new(
let proof = unvalidated::AnchorProof::new(
self.state.chain_id,
root.to_cid()?,
self.state.tx_hash,
Expand Down
Loading

0 comments on commit 474bfd3

Please sign in to comment.