Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: make ChainInclusion trait fully generic and change input to the raw AnchorProof #570

Merged
merged 1 commit into from
Oct 22, 2024
Merged
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
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems backwards

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so? Function takes an eth_rpc::Error and returns a ValidationError

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
Loading