diff --git a/Cargo.lock b/Cargo.lock index 5f110095fc..1f7de73479 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1792,7 +1792,6 @@ name = "cometbls-near" version = "0.1.0" dependencies = [ "borsh 1.5.1", - "cometbls-groth16-verifier", "hex", "hex-literal", "ibc-vm-rs", diff --git a/lib/ibc-vm-rs/src/lib.rs b/lib/ibc-vm-rs/src/lib.rs index c91c36d7cb..c7e95f29d4 100644 --- a/lib/ibc-vm-rs/src/lib.rs +++ b/lib/ibc-vm-rs/src/lib.rs @@ -44,8 +44,8 @@ pub enum IbcError { UnexpectedAction, // TODO(aeryz): this needs context - #[error("client message verification failed")] - ClientMessageVerificationFailed, + #[error("client message verification failed: {0}")] + ClientMessageVerificationFailed(String), #[error("connection ({0}) not found")] ConnectionNotFound(String), @@ -193,7 +193,7 @@ pub enum IbcResponse { valid: bool, }, VerifyClientMessage { - valid: bool, + error: Option, }, CheckForMisbehaviour { misbehaviour_found: bool, diff --git a/lib/ibc-vm-rs/src/states/client_state.rs b/lib/ibc-vm-rs/src/states/client_state.rs index a8239c46f5..57db8772c5 100644 --- a/lib/ibc-vm-rs/src/states/client_state.rs +++ b/lib/ibc-vm-rs/src/states/client_state.rs @@ -67,14 +67,16 @@ impl Runnable for UpdateClient { client_id, client_msg, }, - &[IbcResponse::Status { status }, IbcResponse::VerifyClientMessage { valid }, IbcResponse::CheckForMisbehaviour { misbehaviour_found }], + &[IbcResponse::Status { status }, IbcResponse::VerifyClientMessage { error }, IbcResponse::CheckForMisbehaviour { misbehaviour_found }], ) => { if *status != Status::Active { return Err(IbcError::NotActive(client_id, *status).into()); } - if !valid { - return Err(IbcError::ClientMessageVerificationFailed.into()); + + if let Some(error) = error { + return Err(IbcError::ClientMessageVerificationFailed(error.clone()).into()); } + if *misbehaviour_found { Either::Left(( Self::UpdatedStateOnMisbehaviour { diff --git a/lib/near-verifier/src/error.rs b/lib/near-verifier/src/error.rs index a851400218..f277ff42b5 100644 --- a/lib/near-verifier/src/error.rs +++ b/lib/near-verifier/src/error.rs @@ -1,6 +1,5 @@ use near_account_id::AccountId; use near_primitives_core::hash::CryptoHash; -use unionlabs::near::raw_state_proof::RawStateProof; #[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] pub enum Error { diff --git a/lib/unionlabs/src/uint.rs b/lib/unionlabs/src/uint.rs index 12ef83a2f6..4c7252911e 100644 --- a/lib/unionlabs/src/uint.rs +++ b/lib/unionlabs/src/uint.rs @@ -8,7 +8,6 @@ use core::{ str::FromStr, }; -use borsh::BorshSerialize; use serde::{Deserialize, Serialize}; use serde_utils::HEX_ENCODING_PREFIX; diff --git a/light-clients/cometbls/near/Cargo.toml b/light-clients/cometbls/near/Cargo.toml index 6558926139..2a0cf2a894 100644 --- a/light-clients/cometbls/near/Cargo.toml +++ b/light-clients/cometbls/near/Cargo.toml @@ -10,7 +10,6 @@ crate-type = ["cdylib"] [dependencies] borsh = { workspace = true, features = ["derive"] } -cometbls-groth16-verifier = { workspace = true } hex-literal = { workspace = true } hex.workspace = true ibc-vm-rs = { workspace = true } diff --git a/light-clients/cometbls/near/src/contract.rs b/light-clients/cometbls/near/src/contract.rs index 90b1bbfbf9..2058020b4c 100644 --- a/light-clients/cometbls/near/src/contract.rs +++ b/light-clients/cometbls/near/src/contract.rs @@ -4,7 +4,6 @@ use near_sdk::{ borsh::{BorshDeserialize, BorshSerialize}, env, near_bindgen, store::LookupMap, - PanicOnDefault, }; use unionlabs::{ encoding::{DecodeAs, EncodeAs as _, Proto}, @@ -96,7 +95,10 @@ impl Contract { ), }, IbcQuery::VerifyClientMessage(msg) => IbcResponse::VerifyClientMessage { - valid: self.verify_client_message(&client_id, msg), + error: self + .verify_client_message(&client_id, msg) + .map_err(|e| e.to_string()) + .err(), }, IbcQuery::CheckForMisbehaviour(msg) => IbcResponse::CheckForMisbehaviour { misbehaviour_found: self.check_for_misbehaviour(msg), @@ -106,7 +108,7 @@ impl Contract { .collect() } - pub fn status(&self) -> Status { + fn status(&self) -> Status { Status::Active } @@ -126,7 +128,7 @@ impl Contract { path: MerklePath, value: Vec, // TODO(aeryz): make this a proper error type this is stupid - ) -> bool { + ) -> Result<(), Error> { let Ok(consensus_state) = self .consensus_states .get(client_id) @@ -158,23 +160,30 @@ impl Contract { } // TODO(aeryz): client_msg can be Misbehaviour or Header - fn verify_client_message(&self, client_id: &str, client_msg: Vec) -> bool { - let header = Header::decode_as::(&client_msg).unwrap(); - let client_state = self.client_states.get(client_id).unwrap(); - let consensus_states = self.consensus_states.get(client_id).unwrap(); - let consensus_state = consensus_states.get(&header.trusted_height).unwrap(); + fn verify_client_message(&self, client_id: &str, client_msg: Vec) -> Result<(), Error> { + let header = Header::decode_as::(&client_msg)?; + let client_state = self + .client_states + .get(client_id) + .ok_or(Error::ClientNotFound(client_id.to_string()))?; + let consensus_states = self + .consensus_states + .get(client_id) + .ok_or(Error::ClientNotFound(client_id.to_string()))?; + + let consensus_state = consensus_states + .get(&header.trusted_height) + .ok_or(Error::ConsensusStateNotFound(header.trusted_height))?; // SAFETY: height is bound to be 0..i64::MAX which makes it within the bounds of u64 let untrusted_height_number = header.signed_header.height.inner() as u64; let trusted_height_number = header.trusted_height.revision_height; if untrusted_height_number <= trusted_height_number { - // return Err(InvalidHeaderError::SignedHeaderHeightMustBeMoreRecent { - // signed_height: untrusted_height_number, - // trusted_height: trusted_height_number, - // } - // .into()); - return false; + return Err(Error::SignedHeaderHeightMustBeMoreRecent { + signed_height: untrusted_height_number, + trusted_height: trusted_height_number, + }); } let trusted_timestamp = consensus_state.timestamp; @@ -182,12 +191,10 @@ impl Contract { let untrusted_timestamp = header.signed_header.time.as_unix_nanos(); if untrusted_timestamp <= trusted_timestamp { - // return Err(InvalidHeaderError::SignedHeaderTimestampMustBeMoreRecent { - // signed_timestamp: untrusted_timestamp, - // trusted_timestamp, - // } - // .into()); - return false; + return Err(Error::SignedHeaderTimestampMustBeMoreRecent { + signed_timestamp: untrusted_timestamp, + trusted_timestamp, + }); } if is_client_expired( @@ -195,22 +202,18 @@ impl Contract { client_state.trusting_period, env::block_timestamp(), ) { - // return Err(InvalidHeaderError::HeaderExpired(consensus_state.data.timestamp).into()); - return false; + return Err(Error::HeaderExpired(consensus_state.timestamp)); } let max_clock_drift = env::block_timestamp() .checked_add(client_state.max_clock_drift) - .unwrap(); - // .ok_or(Error::MathOverflow)?; + .ok_or(Error::MathOverflow)?; if untrusted_timestamp >= max_clock_drift { - // return Err(InvalidHeaderError::SignedHeaderCannotExceedMaxClockDrift { - // signed_timestamp: untrusted_timestamp, - // max_clock_drift, - // } - // .into()); - return false; + return Err(Error::SignedHeaderCannotExceedMaxClockDrift { + signed_timestamp: untrusted_timestamp, + max_clock_drift, + }); } let trusted_validators_hash = consensus_state.next_validators_hash; @@ -218,12 +221,10 @@ impl Contract { if untrusted_height_number == trusted_height_number + 1 && header.signed_header.validators_hash != trusted_validators_hash { - // return Err(InvalidHeaderError::InvalidValidatorsHash { - // expected: trusted_validators_hash, - // actual: header.signed_header.validators_hash, - // } - // .into()); - return false; + return Err(Error::InvalidValidatorsHash { + expected: trusted_validators_hash, + actual: header.signed_header.validators_hash, + }); } verify_zkp( @@ -232,13 +233,10 @@ impl Contract { &header.signed_header, header.zero_knowledge_proof, ) - .unwrap(); - - true } #[allow(unused)] - pub fn check_for_misbehaviour(&self, client_msg: Vec) -> bool { + fn check_for_misbehaviour(&self, client_msg: Vec) -> bool { false } diff --git a/light-clients/cometbls/near/src/error.rs b/light-clients/cometbls/near/src/error.rs index 614df75cc5..7b43e072ee 100644 --- a/light-clients/cometbls/near/src/error.rs +++ b/light-clients/cometbls/near/src/error.rs @@ -1,9 +1,11 @@ use unionlabs::{ encoding::{DecodeErrorOf, Proto}, + hash::H256, ibc::{ core::{client::height::Height, commitment::merkle_proof::MerkleProof}, - lightclients::cometbls, + lightclients::cometbls::{self, header::TryFromHeaderError}, }, + TryFromProtoBytesError, }; #[derive(thiserror::Error, Debug, PartialEq)] @@ -20,12 +22,12 @@ pub enum Error { #[error("unable to decode client state")] ClientStateDecode(#[source] DecodeErrorOf), + #[error("client not found")] + ClientNotFound(String), + #[error("Client state not found")] ClientStateNotFound, - #[error("Invalid ZKP: {0:?}")] - InvalidZKP(cometbls_groth16_verifier::Error), - #[error("Consensus state not found for {0}")] ConsensusStateNotFound(Height), @@ -49,4 +51,33 @@ pub enum Error { #[error("invalid timestamp")] InvalidTimestamp, + + #[error( + "trusted validators hash ({expected:?}) does not match the given header's hash ({actual})" + )] + InvalidValidatorsHash { expected: H256, actual: H256 }, + + #[error("signed header ({signed_timestamp}) exceeds the max clock drift ({max_clock_drift})")] + SignedHeaderCannotExceedMaxClockDrift { + signed_timestamp: u64, + max_clock_drift: u64, + }, + + #[error("header with timestamp ({0}) is expired")] + HeaderExpired(u64), + + #[error("signed header with timestamp ({signed_timestamp}) must be more recent than the trusted one ({trusted_timestamp})")] + SignedHeaderTimestampMustBeMoreRecent { + signed_timestamp: u64, + trusted_timestamp: u64, + }, + + #[error("signed header with height ({signed_height}) must be more recent than the trusted one ({trusted_height})")] + SignedHeaderHeightMustBeMoreRecent { + signed_height: u64, + trusted_height: u64, + }, + + #[error("header decode failed {0}")] + HeaderDecode(#[from] TryFromProtoBytesError), } diff --git a/light-clients/near/near/src/contract.rs b/light-clients/near/near/src/contract.rs index fa847bd767..57d9227f5f 100644 --- a/light-clients/near/near/src/contract.rs +++ b/light-clients/near/near/src/contract.rs @@ -80,7 +80,7 @@ impl Contract { ), }, IbcQuery::VerifyClientMessage(msg) => IbcResponse::VerifyClientMessage { - valid: self.verify_client_message(msg), + error: self.verify_client_message(msg), }, IbcQuery::CheckForMisbehaviour(msg) => IbcResponse::CheckForMisbehaviour { misbehaviour_found: self.check_for_misbehaviour(msg), diff --git a/poc-relayer/src/main.rs b/poc-relayer/src/main.rs index e7a05a0757..cfbfafec5e 100644 --- a/poc-relayer/src/main.rs +++ b/poc-relayer/src/main.rs @@ -1,17 +1,10 @@ -use std::{ - collections::HashMap, - str::FromStr, - time::{self, Duration}, -}; +use std::{collections::HashMap, str::FromStr, time::Duration}; use chain_utils::{ - cosmos_sdk::{CosmosSdkChain, CosmosSdkChainExt, CosmosSdkChainRpcs as _, GasConfig}, - private_key::PrivateKey, -}; -use ibc_vm_rs::{ - states::connection_handshake::{self, ConnectionEnd}, - IbcEvent, DEFAULT_IBC_VERSION, + cosmos_sdk::{CosmosSdkChainExt, CosmosSdkChainRpcs as _, GasConfig}, + keyring::{ChainKeyring, KeyringConfig}, }; +use ibc_vm_rs::{states::connection_handshake, DEFAULT_IBC_VERSION}; use near_jsonrpc_client::{methods, JsonRpcClient}; use near_jsonrpc_primitives::types::query::QueryResponseKind; use near_primitives::{ @@ -30,23 +23,20 @@ use tokio::time::sleep; use unionlabs::{ bounded::BoundedI64, cometbls::types::canonical_vote::CanonicalVote, - encoding::{DecodeAs, Encode, EncodeAs, Proto}, - google::protobuf::any::{mk_any, Any, IntoAny}, - hash::H256, + encoding::{DecodeAs, Encode, Proto}, + google::protobuf::any::{mk_any, IntoAny}, ibc::{ core::{ - channel::{self, packet::Packet}, client::height::{Height, IsHeight as _}, - commitment::{merkle_prefix::MerklePrefix, merkle_root::MerkleRoot}, + commitment::merkle_prefix::MerklePrefix, connection::version::Version, }, lightclients::{ - cometbls::{self, client_state::ClientState, consensus_state::ConsensusState}, + cometbls::{self, client_state::ClientState}, near, wasm, }, }, ics24::ClientConsensusStatePath, - id::{ChannelId, ConnectionId, PortId}, near::{ raw_state_proof::RawStateProof, types::{self, MerklePathItem}, @@ -60,10 +50,7 @@ use unionlabs::{ }, traits::Chain, union::galois::{ - poll_request::PollRequest, - poll_response::{PollResponse, ProveRequestDone, ProveRequestFailed}, - prove_request::ProveRequest, - prove_response::ProveResponse, + prove_request::ProveRequest, prove_response::ProveResponse, validator_set_commit::ValidatorSetCommit, }, validated::ValidateT as _, @@ -122,14 +109,15 @@ impl Union { async fn new() -> Self { Union { union: chain_utils::union::Union::new(chain_utils::union::Config { - signers: vec![ - serde_json::from_str( - r#"{ "raw": "0xaa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f" }"#, - ) - .unwrap(), - ], + keyring: KeyringConfig { + name: "alice".to_string(), + keys: vec![ + serde_json::from_str( + r#"{ "raw": "0xaa820fa947beb242032a41b6dc9a8b9c37d8f5fbcda0966b1ec80335b10a7d6f" }"# + ).unwrap() + ], + }, ws_url: WebSocketClientUrl::from_str("ws://localhost:26657/websocket").unwrap(), - prover_endpoint: "http://localhost:9999".to_string(), grpc_url: "http://localhost:9090".to_string(), gas_config: GasConfig { gas_price: 1.0, @@ -137,6 +125,7 @@ impl Union { gas_multiplier: 1.1, max_gas: 400000, }, + prover_endpoints: vec!["http://localhost:9999".to_string()], }) .await .unwrap(), @@ -173,7 +162,7 @@ impl Union { async fn fetch_prove_request(&self, request: ProveRequest) -> ProveResponse { let response = union_prover_api_client::UnionProverApiClient::connect( - self.union.prover_endpoint.clone(), + self.union.prover_endpoints[0].clone(), ) .await .unwrap() @@ -321,7 +310,7 @@ impl Union { async fn create_client(&self, client_state: A, consensus_state: B) { self.union - .signers() + .keyring() .with(|signer| async { let msg = protos::ibc::core::client::v1::MsgCreateClient { client_state: Some(client_state.into_any().into()), @@ -329,9 +318,9 @@ impl Union { signer: signer.to_string(), }; - let tx_hash = self + let (tx_hash, _) = self .union - .broadcast_tx_commit(signer, [mk_any(&msg)]) + .broadcast_tx_commit(signer, [mk_any(&msg)], "".to_string()) .await .unwrap(); @@ -399,7 +388,7 @@ impl Union { consensus_height: Height, ) { self.union - .signers() + .keyring() .with(|signer| async { let msg = protos::ibc::core::connection::v1::MsgConnectionOpenTry { client_id: client_id.to_string(), @@ -436,9 +425,9 @@ impl Union { previous_connection_id: "".to_string(), }; - let tx_hash = self + let (tx_hash, _) = self .union - .broadcast_tx_commit(signer, [mk_any(&msg)]) + .broadcast_tx_commit(signer, [mk_any(&msg)], "".to_string()) .await .unwrap(); @@ -454,7 +443,7 @@ impl Union { proof_height: u64, ) { self.union - .signers() + .keyring() .with(|signer| async { let msg = protos::ibc::core::connection::v1::MsgConnectionOpenConfirm { connection_id: connection_id.to_string(), @@ -469,9 +458,9 @@ impl Union { signer: signer.to_string(), }; - let tx_hash = self + let (tx_hash, _) = self .union - .broadcast_tx_commit(signer, [mk_any(&msg)]) + .broadcast_tx_commit(signer, [mk_any(&msg)], "".to_string()) .await .unwrap(); @@ -482,7 +471,7 @@ impl Union { async fn update_client>(&self, client_id: &str, header: T) { self.union - .signers() + .keyring() .with(|signer| async { let msg = protos::ibc::lightclients::wasm::v1::ClientMessage { data: header.encode(), @@ -494,9 +483,9 @@ impl Union { signer: signer.to_string(), }; - let tx_hash = self + let (tx_hash, _) = self .union - .broadcast_tx_commit(signer, [mk_any(&msg)]) + .broadcast_tx_commit(signer, [mk_any(&msg)], "".to_string()) .await .unwrap();