From 474bfd3db2f78bc2a08e22aa6b5d6e0fd8f75c9c Mon Sep 17 00:00:00 2001 From: Spencer T Brody Date: Mon, 21 Oct 2024 14:45:48 -0400 Subject: [PATCH] refactor: make ChainInclusion trait fully generic and change input to the raw AnchorProof --- anchor-remote/src/cas_remote.rs | 6 +- anchor-service/src/anchor.rs | 6 +- anchor-service/src/cas_mock.rs | 4 +- anchor-service/src/transaction_manager.rs | 4 +- event-svc/src/event/migration.rs | 2 +- event-svc/src/event/mod.rs | 2 +- event-svc/src/event/service.rs | 4 +- event-svc/src/event/validator/event.rs | 6 +- event-svc/src/event/validator/mod.rs | 2 +- event-svc/src/event/validator/time.rs | 70 +++++++--------------- event-svc/src/lib.rs | 2 +- event/src/unvalidated/builder.rs | 4 +- event/src/unvalidated/event.rs | 20 +++---- one/src/daemon.rs | 6 +- validation/src/blockchain/eth_rpc/http.rs | 22 +++---- validation/src/blockchain/eth_rpc/mod.rs | 4 +- validation/src/blockchain/eth_rpc/types.rs | 36 ++++------- 17 files changed, 79 insertions(+), 121 deletions(-) diff --git a/anchor-remote/src/cas_remote.rs b/anchor-remote/src/cas_remote.rs index e53b10817..1470a2ebc 100644 --- a/anchor-remote/src/cas_remote.rs +++ b/anchor-remote/src/cas_remote.rs @@ -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")); @@ -200,11 +200,11 @@ async fn parse_anchor_response(anchor_response: String) -> Result = None; - let mut proof: Option = None; + let mut proof: Option = None; while let Some((cid, block)) = car_reader.next_block().await? { if let Ok(block) = serde_ipld_dagcbor::from_slice::(&block) { detached_time_event = Some(block); - } else if let Ok(block) = serde_ipld_dagcbor::from_slice::(&block) { + } else if let Ok(block) = serde_ipld_dagcbor::from_slice::(&block) { proof = Some(block); } else if let Ok(block) = serde_ipld_dagcbor::from_slice::(&block) { remote_merkle_nodes.insert(cid, block); diff --git a/anchor-service/src/anchor.rs b/anchor-service/src/anchor.rs index fdef2c14f..d21035e93 100644 --- a/anchor-service/src/anchor.rs +++ b/anchor-service/src/anchor.rs @@ -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)] @@ -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, } @@ -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, diff --git a/anchor-service/src/cas_mock.rs b/anchor-service/src/cas_mock.rs index 819b27101..f6aced44f 100644 --- a/anchor-service/src/cas_mock.rs +++ b/anchor-service/src/cas_mock.rs @@ -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, @@ -17,7 +17,7 @@ pub struct MockCas; #[async_trait] impl TransactionManager for MockCas { async fn anchor_root(&self, root_cid: Cid) -> Result { - let mock_proof = Proof::new( + let mock_proof = AnchorProof::new( "mock chain id".to_string(), root_cid, root_cid, diff --git a/anchor-service/src/transaction_manager.rs b/anchor-service/src/transaction_manager.rs index a0d2f3c3e..96ec61681 100644 --- a/anchor-service/src/transaction_manager.rs +++ b/anchor-service/src/transaction_manager.rs @@ -3,7 +3,7 @@ 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; @@ -11,7 +11,7 @@ use crate::anchor::MerkleNodes; /// 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 diff --git a/event-svc/src/event/migration.rs b/event-svc/src/event/migration.rs index 6e11b0ec2..b13dfa294 100644 --- a/event-svc/src/event/migration.rs +++ b/event-svc/src/event/migration.rs @@ -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(); diff --git a/event-svc/src/event/mod.rs b/event-svc/src/event/mod.rs index b9add4e5b..d1a063c12 100644 --- a/event-svc/src/event/mod.rs +++ b/event-svc/src/event/mod.rs @@ -7,4 +7,4 @@ mod store; mod validator; pub use service::{BlockStore, DeliverableRequirement, EventService}; -pub use validator::EthRpcProvider; +pub use validator::ChainInclusionProvider; diff --git a/event-svc/src/event/service.rs b/event-svc/src/event/service.rs index 40a318375..cf7c6bf2f 100644 --- a/event-svc/src/event/service.rs +++ b/event-svc/src/event/service.rs @@ -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}; @@ -89,7 +89,7 @@ impl EventService { pool: SqlitePool, process_undelivered_events: bool, validate_events: bool, - ethereum_rpc_providers: Vec, + ethereum_rpc_providers: Vec, ) -> Result { let event_access = Arc::new(EventAccess::try_new(pool.clone()).await?); diff --git a/event-svc/src/event/validator/event.rs b/event-svc/src/event/validator/event.rs index 7cc7e93f6..d5dfb91d2 100644 --- a/event-svc/src/event/validator/event.rs +++ b/event-svc/src/event/validator/event.rs @@ -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::{ @@ -131,7 +131,7 @@ impl EventValidator { /// Create a new event validator pub async fn try_new( event_access: Arc, - ethereum_rpc_providers: Vec, + ethereum_rpc_providers: Vec, ) -> Result { let time_event_verifier = TimeEventValidator::new_with_providers(ethereum_rpc_providers); @@ -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 } => { diff --git a/event-svc/src/event/validator/mod.rs b/event-svc/src/event/validator/mod.rs index 4608dc4f9..a184717ad 100644 --- a/event-svc/src/event/validator/mod.rs +++ b/event-svc/src/event/validator/mod.rs @@ -4,6 +4,6 @@ mod signed; mod time; -pub use time::EthRpcProvider; +pub use time::ChainInclusionProvider; pub use event::{EventValidator, UnvalidatedEvent, ValidatedEvent, ValidatedEvents}; diff --git a/event-svc/src/event/validator/time.rs b/event-svc/src/event/validator/time.rs index e416fc94a..3ebacc043 100644 --- a/event-svc/src/event/validator/time.rs +++ b/event-svc/src/event/validator/time.rs @@ -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); @@ -20,13 +18,13 @@ impl Timestamp { } } -/// Provider for a remote Ethereum RPC endpoint. -pub type EthRpcProvider = Arc + Send + Sync>; +/// Provider for validating chain inclusion of an AnchorProof on a remote blockchain. +pub type ChainInclusionProvider = Arc; 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, + chain_providers: HashMap, } impl std::fmt::Debug for TimeEventValidator { @@ -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); @@ -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) -> Self { + pub fn new_with_providers(providers: Vec) -> Self { Self { chain_providers: HashMap::from_iter( providers.into_iter().map(|p| (p.chain_id().to_owned(), p)), @@ -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)) } } @@ -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; + async fn get_chain_inclusion_proof(&self, input: &unvalidated::AnchorProof) -> Result; } } async fn get_mock_provider( - input: eth_rpc::EthTxProofInput, + input: unvalidated::AnchorProof, root_cid: Cid, ) -> TimeEventValidator { let mut mock_provider = MockEthRpcProviderTest::new(); @@ -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, }) @@ -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); @@ -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) @@ -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) => { @@ -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) diff --git a/event-svc/src/lib.rs b/event-svc/src/lib.rs index 868aafde5..75275c453 100644 --- a/event-svc/src/lib.rs +++ b/event-svc/src/lib.rs @@ -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 = std::result::Result; diff --git a/event/src/unvalidated/builder.rs b/event/src/unvalidated/builder.rs index 98aeb5b84..f1402fe2a 100644 --- a/event/src/unvalidated/builder.rs +++ b/event/src/unvalidated/builder.rs @@ -316,7 +316,7 @@ impl TimeBuilderState for TimeBuilderWithPrev {} impl TimeBuilder { /// Build the [`unvalidated::TimeEvent`]. pub fn build(self) -> anyhow::Result { - let proof = unvalidated::Proof::new( + let proof = unvalidated::AnchorProof::new( self.state.chain_id, self.state.prev, self.state.tx_hash, @@ -381,7 +381,7 @@ impl TimeBuilder { 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, diff --git a/event/src/unvalidated/event.rs b/event/src/unvalidated/event.rs index 4290a4499..187829516 100644 --- a/event/src/unvalidated/event.rs +++ b/event/src/unvalidated/event.rs @@ -16,7 +16,7 @@ use super::{init, signed, Payload}; /// witness proof. fn get_time_event_witness_blocks( event: &RawTimeEvent, - proof: &Proof, + proof: &AnchorProof, car_blocks: HashMap>, ) -> anyhow::Result> { let mut blocks_in_path = Vec::new(); @@ -182,7 +182,7 @@ where let proof_bytes = car_blocks .get(&event.proof()) .ok_or_else(|| anyhow!("Time Event CAR data missing block for proof"))?; - let proof: Proof = + let proof: AnchorProof = serde_ipld_dagcbor::from_slice(proof_bytes).context("decoding proof")?; let blocks_in_path = get_time_event_witness_blocks(&event, &proof, car_blocks)?; let blocks_in_path = blocks_in_path @@ -335,13 +335,13 @@ impl From for RawEvent { #[derive(Debug)] pub struct TimeEvent { event: RawTimeEvent, - proof: Proof, + proof: AnchorProof, blocks_in_path: Vec, } impl TimeEvent { /// Create a new time event from its parts - pub fn new(event: RawTimeEvent, proof: Proof, blocks_in_path: Vec) -> Self { + pub fn new(event: RawTimeEvent, proof: AnchorProof, blocks_in_path: Vec) -> Self { Self { event, proof, @@ -360,7 +360,7 @@ impl TimeEvent { } /// Get the proof - pub fn proof(&self) -> &Proof { + pub fn proof(&self) -> &AnchorProof { &self.proof } @@ -449,10 +449,10 @@ impl RawTimeEvent { self.path.as_ref() } } -/// Proof data -#[derive(Serialize, Deserialize, Clone)] +/// Proof data for a blockchain transaction used to anchor a merkle tree root onchain. +#[derive(Serialize, Deserialize, Clone, PartialEq)] #[serde(rename_all = "camelCase")] -pub struct Proof { +pub struct AnchorProof { /// eip-155 CHAIN_ID see https://chainid.network https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md chain_id: String, /// the root node of the merkle tree @@ -463,7 +463,7 @@ pub struct Proof { tx_type: String, } -impl Debug for Proof { +impl Debug for AnchorProof { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Proof") .field("chain_id", &self.chain_id) @@ -474,7 +474,7 @@ impl Debug for Proof { } } -impl Proof { +impl AnchorProof { /// Create a proof from its parts. pub fn new(chain_id: String, root: Cid, tx_hash: Cid, tx_type: String) -> Self { Self { diff --git a/one/src/daemon.rs b/one/src/daemon.rs index 85d0646cb..f8d152796 100644 --- a/one/src/daemon.rs +++ b/one/src/daemon.rs @@ -9,7 +9,7 @@ use ceramic_anchor_remote::RemoteCas; use ceramic_anchor_service::AnchorService; use ceramic_core::NodeId; use ceramic_event_svc::eth_rpc::HttpEthRpc; -use ceramic_event_svc::{EthRpcProvider, EventService}; +use ceramic_event_svc::{ChainInclusionProvider, EventService}; use ceramic_interest_svc::InterestService; use ceramic_kubo_rpc::Multiaddr; use ceramic_metrics::{config::Config as MetricsConfig, MetricsHandle}; @@ -245,7 +245,7 @@ pub struct DaemonOpts { async fn get_eth_rpc_providers( ethereum_rpc_urls: Vec, network: &Network, -) -> Result> { +) -> Result> { let ethereum_rpc_urls = if ethereum_rpc_urls.is_empty() { network.default_rpc_urls()? } else { @@ -266,7 +266,7 @@ async fn get_eth_rpc_providers( provider.chain_id(), provider.url() ); - let provider: EthRpcProvider = Arc::new(provider); + let provider: ChainInclusionProvider = Arc::new(provider); providers.push(provider); } else { warn!("Eth RPC provider {} uses chainid {} which isn't supported by Ceramic network {:?}", url, provider_chain,network); diff --git a/validation/src/blockchain/eth_rpc/http.rs b/validation/src/blockchain/eth_rpc/http.rs index de718ba3c..65844872b 100644 --- a/validation/src/blockchain/eth_rpc/http.rs +++ b/validation/src/blockchain/eth_rpc/http.rs @@ -5,6 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; +use crate::eth_rpc::{ChainInclusion, ChainInclusionProof, Error}; use alloy::{ hex, primitives::{BlockHash, TxHash}, @@ -14,14 +15,13 @@ use alloy::{ }; use anyhow::bail; use ceramic_core::Cid; +use ceramic_event::unvalidated::AnchorProof; use lru::LruCache; use multihash_codetable::Multihash; use once_cell::sync::Lazy; use ssi::caip2; use tracing::trace; -use crate::eth_rpc::{ChainInclusion, Error, EthTxProofInput, TimeProof}; - use super::EthProofType; const DAG_CBOR_CODEC: u64 = 0x71; @@ -202,23 +202,21 @@ fn expected_tx_hash(cid: Cid) -> anyhow::Result { #[async_trait::async_trait] impl ChainInclusion for HttpEthRpc { - type InclusionInput = EthTxProofInput; - fn chain_id(&self) -> &caip2::ChainId { &self.chain_id } /// Get the block chain transaction if it exists with the block timestamp information - async fn chain_inclusion_proof(&self, input: &Self::InclusionInput) -> Result { + async fn get_chain_inclusion_proof(&self, input: &AnchorProof) -> Result { // transaction to blockHash, blockNumber, input - let tx_hash = expected_tx_hash(input.tx_hash) + let tx_hash = expected_tx_hash(input.tx_hash()) .map_err(|e| Error::InvalidArgument(format!("invalid transaction hash: {}", e)))?; let tx_hash_res = match self.eth_transaction_by_hash(tx_hash).await? { Some(tx) => tx, None => { return Err(Error::TxNotFound { chain_id: self.chain_id.clone(), - tx_hash: input.tx_hash.to_string(), + tx_hash: input.tx_hash().to_string(), }) } }; @@ -240,26 +238,28 @@ impl ChainInclusion for HttpEthRpc { } }; trace!(?blk_hash_res, "blockByHash response"); - let root_cid = get_root_cid_from_input(&tx_hash_res.input.to_string(), input.tx_type) + let tx_type = EthProofType::from_str(input.tx_type()) + .map_err(|e| Error::InvalidProof(e.to_string()))?; + let root_cid = get_root_cid_from_input(&tx_hash_res.input.to_string(), tx_type) .map_err(|e| Error::InvalidProof(e.to_string()))?; if let Some(threshold) = BLOCK_THRESHHOLDS.get(self.chain_id()) { if blk_hash_res.header.number < *threshold { return Err(Error::InvalidProof("V0 anchor proofs are not supported. Please report this error on the forum: https://forum.ceramic.network/".into())); - } else if input.tx_type != EthProofType::V1 { + } else if tx_type != EthProofType::V1 { return Err(Error::InvalidProof(format!("Any anchor proofs created after block {threshold} for chain {} must include the txType field={}. Anchor txn blockNumber: {}", self.chain_id(), EthProofType::V1, blk_hash_res.header.number))); } } - Ok(TimeProof { + Ok(ChainInclusionProof { timestamp: blk_hash_res.header.timestamp, root_cid, }) } else { Err(Error::TxNotMined { chain_id: self.chain_id.clone(), - tx_hash: input.tx_hash.to_string(), + tx_hash: input.tx_hash().to_string(), }) } } diff --git a/validation/src/blockchain/eth_rpc/mod.rs b/validation/src/blockchain/eth_rpc/mod.rs index a55f43b52..b345b7f31 100644 --- a/validation/src/blockchain/eth_rpc/mod.rs +++ b/validation/src/blockchain/eth_rpc/mod.rs @@ -2,6 +2,4 @@ mod http; mod types; pub use http::HttpEthRpc; -pub use types::{ - BlockHash, ChainInclusion, Error, EthProofType, EthTxProofInput, TimeProof, TxHash, -}; +pub use types::{BlockHash, ChainInclusion, ChainInclusionProof, Error, EthProofType, TxHash}; diff --git a/validation/src/blockchain/eth_rpc/types.rs b/validation/src/blockchain/eth_rpc/types.rs index 6084d4133..af4963752 100644 --- a/validation/src/blockchain/eth_rpc/types.rs +++ b/validation/src/blockchain/eth_rpc/types.rs @@ -6,6 +6,7 @@ use ceramic_core::Cid; use ssi::caip2; pub use alloy::primitives::{BlockHash, TxHash}; +use ceramic_event::unvalidated::AnchorProof; #[derive(Debug)] /// The error variants expected from an Ethereum RPC request @@ -58,15 +59,6 @@ impl From> for Error { } } -#[derive(Clone, Debug, PartialEq, Eq)] -/// Input for an ethereum transaction proof for time events -pub struct EthTxProofInput { - /// The transaction hash as a string (0x prefixed or not) - pub tx_hash: Cid, - /// The time event proof type - pub tx_type: EthProofType, -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] /// The format of the ethereum time event proof pub enum EthProofType { @@ -101,36 +93,28 @@ impl FromStr for EthProofType { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -/// A proof of time on the blockchain -pub struct TimeProof { +/// A proof of time derived from state on the blockchain +pub struct ChainInclusionProof { /// The timestamp the proof was recorded pub timestamp: u64, /// The root CID of the proof pub root_cid: Cid, } -/* - Planning to implemented this trait for Hoku using something like: - - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct HokuTxProof { - cid: Cid, - index: u64, - } -*/ #[async_trait::async_trait] -/// Ethereum RPC provider methods. This is a higher level type than the actual RPC calls neeed and +/// Wrapper around blockchain RPC provider that can be used to query blockchain state for the +/// relevant information (such as the timestamp) for the given transaction described by an +/// `AnchorProof`. This is a higher level type than the actual RPC calls need and /// may wrap a multiple calls into a logical behavior of getting necessary information. pub trait ChainInclusion { - /// The input format needed to do the inclusion proof - type InclusionInput; - /// Get the CAIP2 chain ID supported by this RPC provider fn chain_id(&self) -> &caip2::ChainId; /// Get the block chain transaction if it exists with the block timestamp information - async fn chain_inclusion_proof(&self, input: &Self::InclusionInput) - -> Result; + async fn get_chain_inclusion_proof( + &self, + input: &AnchorProof, + ) -> Result; } #[cfg(test)]