Skip to content

Commit

Permalink
feat: scaffold top-level errors (#791)
Browse files Browse the repository at this point in the history
also started working on tendermint json serde, will pick up at a later
date as it is quite a mess
  • Loading branch information
benluelo authored Oct 11, 2023
2 parents d81df13 + fc2dcb3 commit 9c80ca3
Show file tree
Hide file tree
Showing 16 changed files with 305 additions and 72 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/chain-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ chrono = { version = "0.4.26", default-features = false, features = ["alloc"] }
hubble.workspace = true
reqwest = "0.11.20"
serde_json = "1.0.107"
thiserror = "1.0.49"
20 changes: 14 additions & 6 deletions lib/chain-utils/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use ethers::{
contract::{ContractError, EthCall, EthLogDecode},
core::k256::ecdsa,
middleware::SignerMiddleware,
providers::{Middleware, Provider, Ws},
providers::{Middleware, Provider, ProviderError, Ws, WsClientError},
signers::{LocalWallet, Wallet},
utils::secret_key_to_address,
};
Expand Down Expand Up @@ -279,11 +279,19 @@ impl<C: ChainSpec> Chain for Evm<C> {
}
}

#[derive(Debug, thiserror::Error)]
pub enum EvmInitError {
#[error("unable to connect to websocket")]
Ws(#[from] WsClientError),
#[error("provider error")]
Provider(#[from] ProviderError),
}

impl<C: ChainSpec> Evm<C> {
pub async fn new(config: Config) -> Self {
let provider = Provider::new(Ws::connect(config.eth_rpc_api).await.unwrap());
pub async fn new(config: Config) -> Result<Self, EvmInitError> {
let provider = Provider::new(Ws::connect(config.eth_rpc_api).await?);

let chain_id = provider.get_chainid().await.unwrap();
let chain_id = provider.get_chainid().await?;

let signing_key: ecdsa::SigningKey = config.signer.value();
let address = secret_key_to_address(&signing_key);
Expand All @@ -292,7 +300,7 @@ impl<C: ChainSpec> Evm<C> {

let signer_middleware = Arc::new(SignerMiddleware::new(provider.clone(), wallet.clone()));

Self {
Ok(Self {
chain_id: U256(chain_id),
ibc_handler: IBCHandler::new(config.ibc_handler_address, signer_middleware.clone()),
provider,
Expand All @@ -305,7 +313,7 @@ impl<C: ChainSpec> Evm<C> {
hasura_config.secret,
)
}),
}
})
}

// TODO: Change to take a beacon slot instead of a height
Expand Down
9 changes: 8 additions & 1 deletion lib/chain-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ pub struct ChainEvent<C: Chain> {
}

pub trait ClientState {
type ChainId: Debug + Display + PartialEq + Hash + Clone + Serialize + for<'de> Deserialize<'de>;
type ChainId: Debug
+ Display
+ PartialEq
+ Eq
+ Hash
+ Clone
+ Serialize
+ for<'de> Deserialize<'de>;
type Height: IsHeight;

fn height(&self) -> Self::Height;
Expand Down
52 changes: 35 additions & 17 deletions lib/chain-utils/src/union.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::{fmt::Display, str::FromStr};
use std::{fmt::Display, num::ParseIntError, str::FromStr};

use ethers::prelude::k256::ecdsa;
use futures::{stream, Future, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt};
use prost::Message;
use protos::ibc::core::channel::v1::QueryPacketAcknowledgementRequest;
use serde::{Deserialize, Serialize};
use sha2::Digest;
use tendermint_rpc::{
Expand Down Expand Up @@ -290,35 +289,54 @@ impl Chain for Union {
}
}

#[derive(Debug, thiserror::Error)]
pub enum UnionInitError {
#[error("tendermint rpc error")]
Tendermint(#[from] tendermint_rpc::Error),
#[error(
"unable to parse chain id: expected format `<chain>-<revision-number>`, found `{found}`"
)]
// TODO: Once the `Id` trait in unionlabs is cleaned up to no longer use static id types, this error should just wrap `IdParseError`
ChainIdParse {
found: String,
#[source]
source: Option<ParseIntError>,
},
}

impl Union {
pub async fn new(config: Config) -> Self {
pub async fn new(config: Config) -> Result<Self, UnionInitError> {
let (tm_client, driver) = WebSocketClient::builder(config.ws_url)
.compat_mode(tendermint_rpc::client::CompatMode::V0_37)
.build()
.await
.unwrap();
.await?;

tokio::spawn(async move { driver.run().await });

let chain_id = tm_client
.status()
.await
.unwrap()
.node_info
.network
.to_string();

let chain_revision = chain_id.split('-').last().unwrap().parse().unwrap();

Self {
let chain_id = tm_client.status().await?.node_info.network.to_string();

let chain_revision = chain_id
.split('-')
.last()
.ok_or_else(|| UnionInitError::ChainIdParse {
found: chain_id.clone(),
source: None,
})?
.parse()
.map_err(|err| UnionInitError::ChainIdParse {
found: chain_id.clone(),
source: Some(err),
})?;

Ok(Self {
signer: CosmosAccountId::new(config.signer.value(), "union".to_string()),
tm_client,
chain_id,
chain_revision,
prover_endpoint: config.prover_endpoint,
grpc_url: config.grpc_url,
fee_denom: config.fee_denom,
}
})
}

pub async fn broadcast_tx_commit(
Expand Down
11 changes: 10 additions & 1 deletion lib/serde-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const HEX_ENCODING_PREFIX: &str = "0x";
pub enum FromHexStringError {
Hex(FromHexError),
MissingPrefix(String),
EmptyString,
// NOTE: Contains the stringified error
TryFromBytes(String),
}
Expand All @@ -18,6 +19,7 @@ impl std::error::Error for FromHexStringError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FromHexStringError::Hex(hex) => Some(hex),
FromHexStringError::EmptyString => None,
FromHexStringError::MissingPrefix(_) => None,
FromHexStringError::TryFromBytes(_) => None,
}
Expand All @@ -28,6 +30,7 @@ impl core::fmt::Display for FromHexStringError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FromHexStringError::Hex(e) => write!(f, "{e}"),
FromHexStringError::EmptyString => write!(f, "cannot parse empty string as hex"),
FromHexStringError::MissingPrefix(data) => write!(
f,
"missing prefix `{HEX_ENCODING_PREFIX}` when deserializing hex data '{data}'",
Expand All @@ -51,7 +54,13 @@ where
T: TryFrom<Vec<u8>>,
<T as TryFrom<Vec<u8>>>::Error: Debug + 'static,
{
match string.as_ref().strip_prefix(HEX_ENCODING_PREFIX.as_bytes()) {
let s = &string.as_ref();

if s.is_empty() {
return Err(FromHexStringError::EmptyString);
}

match s.strip_prefix(HEX_ENCODING_PREFIX.as_bytes()) {
Some(maybe_hex) => hex::decode(maybe_hex)
.map_err(FromHexStringError::Hex)?
.try_into()
Expand Down
21 changes: 21 additions & 0 deletions lib/unionlabs/src/bounded_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ macro_rules! bounded_int {
}
}

impl<const MIN: $ty, const MAX: $ty> std::str::FromStr for $Struct<MIN, MAX> {
type Err = BoundedIntParseError<$ty>;

fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<$ty>()
.map_err(BoundedIntParseError::Parse)
.and_then(|n| n.try_into().map_err(BoundedIntParseError::Value))
}
}

impl<const MIN: $ty, const MAX: $ty> std::fmt::Display for $Struct<MIN, MAX> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

$(
const _: () = assert!(
Expand Down Expand Up @@ -129,6 +144,12 @@ pub struct BoundedIntError<T> {
found: T,
}

#[derive(Debug, Clone, PartialEq)]
pub enum BoundedIntParseError<T> {
Parse(std::num::ParseIntError),
Value(BoundedIntError<T>),
}

bounded_int! {
pub BoundedI8(i8);
pub BoundedI16(i16);
Expand Down
1 change: 1 addition & 0 deletions lib/unionlabs/src/tendermint/types/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct Header {
/// basic block info
pub version: Consensus,
pub chain_id: String,
#[serde(with = "::serde_utils::string")]
pub height: BoundedI64<0, { i64::MAX }>,
pub time: Timestamp,
/// prev block info
Expand Down
2 changes: 2 additions & 0 deletions lib/unionlabs/src/tendermint/version/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::{Proto, TypeUrl};

#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Consensus {
#[serde(with = "::serde_utils::string")]
pub block: u64,
#[serde(with = "::serde_utils::string", default)]
pub app: u64,
}

Expand Down
26 changes: 19 additions & 7 deletions voyager/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ use std::{
str::FromStr,
};

use chain_utils::{evm::Evm, union::Union, Chain};
use chain_utils::{
evm::{Evm, EvmInitError},
union::{Union, UnionInitError},
Chain,
};
use futures::Future;
use serde::{Deserialize, Serialize};
use unionlabs::{
Expand Down Expand Up @@ -36,12 +40,20 @@ pub enum AnyChain {
EvmMinimal(Evm<Minimal>),
}

#[derive(Debug, thiserror::Error)]
pub enum AnyChainTryFromConfigError {
#[error("error initializing a union chain")]
Union(#[from] UnionInitError),
#[error("error initializing an ethereum chain")]
Evm(#[from] EvmInitError),
}

impl AnyChain {
pub async fn try_from_config<Q: Queue>(
voyager_config: &config::VoyagerConfig<Q>,
config: ChainConfig,
) -> Self {
match config {
) -> Result<Self, AnyChainTryFromConfigError> {
Ok(match config {
ChainConfig::Evm(EvmChainConfig::Mainnet(evm)) => Self::EvmMainnet(
Evm::<Mainnet>::new(chain_utils::evm::Config {
ibc_handler_address: evm.ibc_handler_address,
Expand All @@ -50,7 +62,7 @@ impl AnyChain {
eth_beacon_rpc_api: evm.eth_beacon_rpc_api,
hasura_config: voyager_config.hasura.clone(),
})
.await,
.await?,
),
ChainConfig::Evm(EvmChainConfig::Minimal(evm)) => Self::EvmMinimal(
Evm::<Minimal>::new(chain_utils::evm::Config {
Expand All @@ -60,7 +72,7 @@ impl AnyChain {
eth_beacon_rpc_api: evm.eth_beacon_rpc_api,
hasura_config: voyager_config.hasura.clone(),
})
.await,
.await?,
),
ChainConfig::Union(union) => Self::Union(
Union::new(chain_utils::union::Config {
Expand All @@ -70,9 +82,9 @@ impl AnyChain {
grpc_url: union.grpc_url,
fee_denom: union.fee_denom,
})
.await,
.await?,
),
}
})
}
}

Expand Down
Loading

0 comments on commit 9c80ca3

Please sign in to comment.