feat: error reporting with eyre (#240)

Co-authored-by: hu55a1n1 <sufialhussaini@gmail.com>
This commit is contained in:
Daniel Gushchyan 2024-10-08 01:53:49 -07:00 committed by GitHub
parent 634bc6a8f3
commit df40e592c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 322 additions and 383 deletions

1
Cargo.lock generated
View file

@ -1285,6 +1285,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"color-eyre",
"cosmos-sdk-proto", "cosmos-sdk-proto",
"cosmrs", "cosmrs",
"hex", "hex",

View file

@ -1,5 +1,6 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use color_eyre::{eyre::eyre, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::{ use tokio::{
fs::File, fs::File,
@ -8,7 +9,7 @@ use tokio::{
use tracing::debug; use tracing::debug;
use xxhash_rust::xxh3::Xxh3; use xxhash_rust::xxh3::Xxh3;
use crate::{config::Config, error::Error}; use crate::config::Config;
const BUFFER_SIZE: usize = 16384; // 16 KB buffer const BUFFER_SIZE: usize = 16384; // 16 KB buffer
type Hash = u64; type Hash = u64;
@ -22,7 +23,7 @@ struct DeployedContract {
// Porcelain // Porcelain
impl Config { impl Config {
pub async fn contract_has_changed(&self, file: &Path) -> Result<bool, Error> { pub async fn contract_has_changed(&self, file: &Path) -> Result<bool> {
let cur_hash: Hash = Self::gen_hash(file).await?; let cur_hash: Hash = Self::gen_hash(file).await?;
debug!("current file hash: {}", cur_hash); debug!("current file hash: {}", cur_hash);
@ -39,20 +40,17 @@ impl Config {
} }
/// Return a hash of the given file's contents /// Return a hash of the given file's contents
pub async fn gen_hash(file: &Path) -> Result<Hash, Error> { pub async fn gen_hash(file: &Path) -> Result<Hash> {
let file = File::open(file) let file = File::open(file).await?;
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
let mut reader = BufReader::new(file); let mut reader = BufReader::new(file);
let mut hasher = Xxh3::new(); let mut hasher = Xxh3::new();
let mut buffer = [0; BUFFER_SIZE]; let mut buffer = [0; BUFFER_SIZE];
loop { loop {
let bytes_read = reader let bytes_read = reader.read(&mut buffer).await?;
.read(&mut buffer)
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if bytes_read == 0 { if bytes_read == 0 {
break; break;
} }
@ -65,7 +63,7 @@ impl Config {
Ok(hash) Ok(hash)
} }
pub async fn save_codeid_to_cache(&self, file: &Path, code_id: u64) -> Result<(), Error> { pub async fn save_codeid_to_cache(&self, file: &Path, code_id: u64) -> Result<()> {
let contract_hash = Self::gen_hash(file).await?; let contract_hash = Self::gen_hash(file).await?;
let dest = Self::to_cache_path(self, file)?; let dest = Self::to_cache_path(self, file)?;
let deployed_contract = DeployedContract { let deployed_contract = DeployedContract {
@ -76,7 +74,7 @@ impl Config {
Self::write_to_cache(dest.as_path(), &deployed_contract).await Self::write_to_cache(dest.as_path(), &deployed_contract).await
} }
pub async fn get_cached_codeid(&self, file: &Path) -> Result<u64, Error> { pub async fn get_cached_codeid(&self, file: &Path) -> Result<u64> {
let cache_path = Self::to_cache_path(self, file)?; let cache_path = Self::to_cache_path(self, file)?;
let code_id = Self::read_from_cache(cache_path.as_path()).await?.code_id; let code_id = Self::read_from_cache(cache_path.as_path()).await?.code_id;
@ -85,12 +83,16 @@ impl Config {
// Plumbing // Plumbing
fn to_cache_path(&self, file: &Path) -> Result<PathBuf, Error> { fn to_cache_path(&self, file: &Path) -> Result<PathBuf> {
// Get cache filepath (".quartz/cache/example.wasm.json") from "example.wasm" filepath // Get cache filepath (".quartz/cache/example.wasm.json") from "example.wasm" filepath
let mut filename = file let mut filename = file
.file_name() .file_name()
.ok_or(Error::PathNotFile(file.display().to_string()))? .ok_or(eyre!(
"file at cache filepath does not exist {}",
file.display()
))?
.to_os_string(); .to_os_string();
filename.push(".json"); filename.push(".json");
let cached_file_path = Self::cache_dir(self)?.join::<PathBuf>(filename.into()); let cached_file_path = Self::cache_dir(self)?.join::<PathBuf>(filename.into());
@ -99,42 +101,43 @@ impl Config {
} }
/// Retreive hash from cache file /// Retreive hash from cache file
async fn read_from_cache(cache_file: &Path) -> Result<DeployedContract, Error> { async fn read_from_cache(cache_file: &Path) -> Result<DeployedContract> {
let content = tokio::fs::read_to_string(cache_file) let content = tokio::fs::read_to_string(cache_file).await?;
.await
.map_err(|e| Error::GenericErr(e.to_string()))?; serde_json::from_str(&content).map_err(|e| eyre!(e))
serde_json::from_str(&content).map_err(|e| Error::GenericErr(e.to_string()))
} }
/// Write a given file's contents hash to a file in cache directory /// Write a given file's contents hash to a file in cache directory
async fn write_to_cache(cache_file: &Path, data: &DeployedContract) -> Result<(), Error> { async fn write_to_cache(cache_file: &Path, data: &DeployedContract) -> Result<()> {
let content = serde_json::to_string(data).map_err(|e| Error::GenericErr(e.to_string()))?; let content = serde_json::to_string(data)?;
tokio::fs::write(cache_file, content) tokio::fs::write(cache_file, content)
.await .await
.map_err(|e| Error::GenericErr(e.to_string())) .map_err(|e| eyre!(e))
}
pub fn cache_dir(&self) -> Result<PathBuf> {
let cache_dir = self.app_dir.join(".cache/");
std::fs::create_dir_all(&cache_dir)?;
Ok(cache_dir)
} }
pub fn cache_dir(&self) -> Result<PathBuf, Error> { pub fn build_log_dir(&self) -> Result<PathBuf> {
Ok(self.app_dir.join(".cache/")) let build_log_dir = self.app_dir.join(".cache/log/");
} std::fs::create_dir_all(&build_log_dir)?;
Ok(build_log_dir)
pub fn build_log_dir(&self) -> Result<PathBuf, Error> {
Ok(self.app_dir.join(".cache/log/"))
} }
/// Creates the build log if it isn't created already, returns relative path from app_dir to log directory /// Creates the build log if it isn't created already, returns relative path from app_dir to log directory
pub async fn create_build_log(&self) -> Result<PathBuf, Error> { pub async fn create_build_log(&self) -> Result<PathBuf> {
let log_dir = Self::build_log_dir(self)?; let log_dir = Self::build_log_dir(self)?;
if !log_dir.exists() { if !log_dir.exists() {
tokio::fs::create_dir_all(&log_dir) tokio::fs::create_dir_all(&log_dir).await?;
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
} }
Ok(log_dir) Ok(log_dir)
} }
pub async fn log_build(&self, is_enclave: bool) -> Result<(), Error> { pub async fn log_build(&self, is_enclave: bool) -> Result<()> {
let log_dir = Self::create_build_log(self).await?; let log_dir = Self::create_build_log(self).await?;
let filename = match is_enclave { let filename = match is_enclave {
@ -142,9 +145,7 @@ impl Config {
false => "contract", false => "contract",
}; };
tokio::fs::write(log_dir.join(filename), "test") tokio::fs::write(log_dir.join(filename), "log").await?;
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
Ok(()) Ok(())
} }

View file

@ -79,7 +79,7 @@ fn default_ws_url() -> Url {
} }
fn default_grpc_url() -> Url { fn default_grpc_url() -> Url {
"http://127.0.0.1:9090," "http://127.0.0.1:9090"
.parse() .parse()
.expect("valid hardcoded URL") .expect("valid hardcoded URL")
} }

View file

@ -1,58 +0,0 @@
use displaydoc::Display;
use thiserror::Error;
#[derive(Debug, Display, Error)]
pub enum Error {
/// Specified path `{0}` is not a directory
PathNotDir(String),
/// Specified file `{0}` does not exist
PathNotFile(String),
/// unspecified error: {0}
GenericErr(String),
/// IoError: {0}
IoError(String),
/// TOML Error : {0}
TomlError(String),
/// Tendermint error: {0}
TendermintError(String),
/// Clearscreen error: {0}
ClearscreenError(String),
/// JSON Error: {0}
JsonError(String),
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IoError(err.to_string())
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Self {
Error::TomlError(err.to_string())
}
}
impl From<toml::ser::Error> for Error {
fn from(err: toml::ser::Error) -> Self {
Error::TomlError(err.to_string())
}
}
impl From<tendermint::Error> for Error {
fn from(err: tendermint::Error) -> Self {
Error::TendermintError(err.to_string())
}
}
impl From<clearscreen::Error> for Error {
fn from(err: clearscreen::Error) -> Self {
Error::ClearscreenError(err.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::JsonError(err.to_string())
}
}

View file

@ -1,6 +1,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use color_eyre::{Report, Result};
use crate::{config::Config, error::Error, request::Request, response::Response}; use crate::{config::Config, request::Request, response::Response};
pub mod utils; pub mod utils;
// commands // commands
@ -14,24 +15,16 @@ pub mod init;
#[async_trait] #[async_trait]
pub trait Handler { pub trait Handler {
type Error;
type Response; type Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report>;
self,
config: C,
) -> Result<Self::Response, Self::Error>;
} }
#[async_trait] #[async_trait]
impl Handler for Request { impl Handler for Request {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
match self { match self {
Request::Init(request) => request.handle(config).await, Request::Init(request) => request.handle(config).await,
Request::Handshake(request) => request.handle(config).await, Request::Handshake(request) => request.handle(config).await,

View file

@ -1,12 +1,11 @@
use std::process::Command; use std::process::Command;
use async_trait::async_trait; use async_trait::async_trait;
use color_eyre::owo_colors::OwoColorize; use color_eyre::{eyre::eyre, owo_colors::OwoColorize, Report, Result};
use tracing::{debug, info}; use tracing::{debug, info};
use crate::{ use crate::{
config::Config, config::Config,
error::Error,
handler::Handler, handler::Handler,
request::contract_build::ContractBuildRequest, request::contract_build::ContractBuildRequest,
response::{contract_build::ContractBuildResponse, Response}, response::{contract_build::ContractBuildResponse, Response},
@ -14,13 +13,9 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for ContractBuildRequest { impl Handler for ContractBuildRequest {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref(); let config = config.as_ref();
info!("{}", "\nPeforming Contract Build".blue().bold()); info!("{}", "\nPeforming Contract Build".blue().bold());
@ -46,15 +41,10 @@ impl Handler for ContractBuildRequest {
} }
info!("{}", "🚧 Building contract binary ...".green().bold()); info!("{}", "🚧 Building contract binary ...".green().bold());
let status = command let status = command.status()?;
.status()
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() { if !status.success() {
return Err(Error::GenericErr(format!( return Err(eyre!("Couldn't build contract. \n{:?}", status));
"Couldn't build contract. \n{:?}",
status
)));
} }
config.log_build(false).await?; config.log_build(false).await?;

View file

@ -2,7 +2,11 @@ use std::path::Path;
use async_trait::async_trait; use async_trait::async_trait;
use cargo_metadata::MetadataCommand; use cargo_metadata::MetadataCommand;
use color_eyre::owo_colors::OwoColorize; use color_eyre::{
eyre::{eyre, Context},
owo_colors::OwoColorize,
Report, Result,
};
use cw_client::{CliClient, CwClient}; use cw_client::{CliClient, CwClient};
use serde_json::json; use serde_json::json;
use tendermint_rpc::HttpClient; use tendermint_rpc::HttpClient;
@ -11,7 +15,6 @@ use tracing::{debug, info};
use super::utils::{helpers::block_tx_commit, types::WasmdTxResponse}; use super::utils::{helpers::block_tx_commit, types::WasmdTxResponse};
use crate::{ use crate::{
config::Config, config::Config,
error::Error,
handler::{utils::relay::RelayMessage, Handler}, handler::{utils::relay::RelayMessage, Handler},
request::contract_deploy::ContractDeployRequest, request::contract_deploy::ContractDeployRequest,
response::{contract_deploy::ContractDeployResponse, Response}, response::{contract_deploy::ContractDeployResponse, Response},
@ -19,24 +22,18 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for ContractDeployRequest { impl Handler for ContractDeployRequest {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref(); let config = config.as_ref();
info!("{}", "\nPeforming Contract Deploy".blue().bold()); info!("{}", "\nPeforming Contract Deploy".blue().bold());
// Get contract package name in snake_case // Get contract package name in snake_case
let package_name = MetadataCommand::new() let package_name = MetadataCommand::new()
.manifest_path(&self.contract_manifest) .manifest_path(&self.contract_manifest)
.exec() .exec()?
.map_err(|e| Error::GenericErr(e.to_string()))?
.root_package() .root_package()
.ok_or("No root package found in the metadata") .ok_or(eyre!("No root package found in the metadata"))?
.map_err(|e| Error::GenericErr(e.to_string()))?
.name .name
.clone() .clone()
.replace('-', "_"); .replace('-', "_");
@ -47,9 +44,7 @@ impl Handler for ContractDeployRequest {
.join(package_name) .join(package_name)
.with_extension("wasm"); .with_extension("wasm");
let (code_id, contract_addr) = deploy(wasm_bin_path.as_path(), self, config) let (code_id, contract_addr) = deploy(wasm_bin_path.as_path(), self, config).await?;
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
Ok(ContractDeployResponse { Ok(ContractDeployResponse {
code_id, code_id,
@ -63,17 +58,23 @@ async fn deploy(
wasm_bin_path: &Path, wasm_bin_path: &Path,
args: ContractDeployRequest, args: ContractDeployRequest,
config: &Config, config: &Config,
) -> Result<(u64, String), anyhow::Error> { ) -> Result<(u64, String), Report> {
let tmrpc_client = HttpClient::new(config.node_url.as_str())?; let tmrpc_client = HttpClient::new(config.node_url.as_str())?;
let cw_client = CliClient::neutrond(config.node_url.clone()); let cw_client = CliClient::neutrond(config.node_url.clone());
info!("🚀 Deploying {} Contract", args.label); info!("🚀 Deploying {} Contract", args.label);
let code_id = if config.contract_has_changed(wasm_bin_path).await? { let code_id = if config.contract_has_changed(wasm_bin_path).await? {
let deploy_output: WasmdTxResponse = serde_json::from_str(&cw_client.deploy( let deploy_output: WasmdTxResponse = serde_json::from_str(
&config.chain_id, &cw_client
&config.tx_sender, .deploy(
wasm_bin_path.display().to_string(), &config.chain_id,
)?)?; &config.tx_sender,
wasm_bin_path.display().to_string(),
)
.map_err(|err| eyre!(Box::new(err)))?,
)
.wrap_err("Error calling deploy on cw client")?;
let res = block_tx_commit(&tmrpc_client, deploy_output.txhash).await?; let res = block_tx_commit(&tmrpc_client, deploy_output.txhash).await?;
// Find the 'code_id' attribute // Find the 'code_id' attribute
@ -89,15 +90,21 @@ async fn deploy(
.find(|attr| attr.key_str().unwrap_or("") == "code_id") .find(|attr| attr.key_str().unwrap_or("") == "code_id")
}) })
.and_then(|attr| attr.value_str().ok().and_then(|v| v.parse().ok())) .and_then(|attr| attr.value_str().ok().and_then(|v| v.parse().ok()))
.ok_or_else(|| anyhow::anyhow!("Failed to find code_id in the transaction result"))?; .ok_or_else(|| eyre!("Failed to find code_id in the transaction result"))?;
info!("Code ID: {}", code_id); info!("Code ID: {}", code_id);
config.save_codeid_to_cache(wasm_bin_path, code_id).await?; config
.save_codeid_to_cache(wasm_bin_path, code_id)
.await
.wrap_err("Error saving contract code id to cache")?;
code_id code_id
} else { } else {
config.get_cached_codeid(wasm_bin_path).await? config
.get_cached_codeid(wasm_bin_path)
.await
.wrap_err("Error getting contract code id from cache")?
}; };
info!("🚀 Communicating with Relay to Instantiate..."); info!("🚀 Communicating with Relay to Instantiate...");
@ -116,6 +123,7 @@ async fn deploy(
json!(init_msg), json!(init_msg),
&format!("{} Contract #{}", args.label, code_id), &format!("{} Contract #{}", args.label, code_id),
)?)?; )?)?;
let res = block_tx_commit(&tmrpc_client, init_output.txhash).await?; let res = block_tx_commit(&tmrpc_client, init_output.txhash).await?;
// Find the '_contract_address' attribute // Find the '_contract_address' attribute
@ -131,9 +139,7 @@ async fn deploy(
.find(|attr| attr.key_str().unwrap_or("") == "_contract_address") .find(|attr| attr.key_str().unwrap_or("") == "_contract_address")
}) })
.and_then(|attr| attr.value_str().ok().and_then(|v| v.parse().ok())) .and_then(|attr| attr.value_str().ok().and_then(|v| v.parse().ok()))
.ok_or_else(|| { .ok_or_else(|| eyre!("Failed to find contract_address in the transaction result"))?;
anyhow::anyhow!("Failed to find contract_address in the transaction result")
})?;
info!("🚀 Successfully deployed and instantiated contract!"); info!("🚀 Successfully deployed and instantiated contract!");
info!("🆔 Code ID: {}", code_id); info!("🆔 Code ID: {}", code_id);

View file

@ -1,9 +1,11 @@
use std::{path::PathBuf, time::Duration}; use std::{path::PathBuf, time::Duration};
use async_trait::async_trait; use async_trait::async_trait;
use color_eyre::owo_colors::OwoColorize; use color_eyre::{
// todo get rid of this? eyre::{eyre, Context},
use miette::{IntoDiagnostic, Result}; owo_colors::OwoColorize,
Report, Result,
};
use quartz_common::proto::core_client::CoreClient; use quartz_common::proto::core_client::CoreClient;
use tokio::{sync::mpsc, time::sleep}; use tokio::{sync::mpsc, time::sleep};
use tracing::{debug, info}; use tracing::{debug, info};
@ -11,7 +13,6 @@ use watchexec::Watchexec;
use watchexec_signals::Signal; use watchexec_signals::Signal;
use crate::{ use crate::{
error::Error,
handler::{utils::helpers::wasmaddr_to_id, Handler}, handler::{utils::helpers::wasmaddr_to_id, Handler},
request::{ request::{
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest, contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
@ -24,13 +25,9 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for DevRequest { impl Handler for DevRequest {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref(); let config = config.as_ref();
info!("\nPeforming Dev"); info!("\nPeforming Dev");
@ -58,7 +55,7 @@ async fn dev_driver(
mut rx: mpsc::Receiver<DevRebuild>, mut rx: mpsc::Receiver<DevRebuild>,
args: &DevRequest, args: &DevRequest,
config: Config, config: Config,
) -> Result<(), Error> { ) -> Result<(), Report> {
// State // State
let mut first_enclave_message = true; let mut first_enclave_message = true;
let mut first_contract_message = true; let mut first_contract_message = true;
@ -82,7 +79,10 @@ async fn dev_driver(
let contract_build = ContractBuildRequest { let contract_build = ContractBuildRequest {
contract_manifest: args.contract_manifest.clone(), contract_manifest: args.contract_manifest.clone(),
}; };
contract_build.handle(&config).await?; contract_build
.handle(&config)
.await
.wrap_err("Could not run `contract build`")?;
// Start enclave in background // Start enclave in background
spawn_enclave_start(args, &config)?; spawn_enclave_start(args, &config)?;
@ -99,9 +99,7 @@ async fn dev_driver(
info!("{}", "Enclave is listening for requests...".green().bold()); info!("{}", "Enclave is listening for requests...".green().bold());
} }
Err(e) => { Err(e) => {
eprintln!("Error launching quartz app"); return Err(e).wrap_err("Error initializing `quartz dev`");
return Err(e);
} }
} }
} }
@ -132,9 +130,7 @@ async fn dev_driver(
info!("{}", "Enclave is listening for requests...".green().bold()); info!("{}", "Enclave is listening for requests...".green().bold());
} }
Err(e) => { Err(e) => {
eprintln!("Error restarting enclave and handshake"); return Err(e).wrap_err("Error restarting enclave after rebuild");
return Err(e);
} }
} }
} }
@ -154,7 +150,7 @@ async fn dev_driver(
Err(e) => { Err(e) => {
eprintln!("Error deploying contract and handshake:"); eprintln!("Error deploying contract and handshake:");
return Err(e); return Err(e).wrap_err("Error redeploying contract after rebuild");
} }
} }
@ -167,7 +163,7 @@ async fn dev_driver(
} }
// Spawns enclve start in a separate task which runs in the background // Spawns enclve start in a separate task which runs in the background
fn spawn_enclave_start(args: &DevRequest, config: &Config) -> Result<(), Error> { fn spawn_enclave_start(args: &DevRequest, config: &Config) -> Result<()> {
// In separate process, launch the enclave // In separate process, launch the enclave
let enclave_start = EnclaveStartRequest { let enclave_start = EnclaveStartRequest {
unsafe_trust_latest: args.unsafe_trust_latest, unsafe_trust_latest: args.unsafe_trust_latest,
@ -180,10 +176,8 @@ fn spawn_enclave_start(args: &DevRequest, config: &Config) -> Result<(), Error>
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = enclave_start.handle(config_cpy).await { if let Err(e) = enclave_start.handle(config_cpy).await {
eprintln!("Error running enclave start.\n {}", e); eprintln!("Error running enclave start.\n {:?}", e);
} }
Ok::<(), Error>(())
}); });
Ok(()) Ok(())
@ -194,7 +188,7 @@ async fn deploy_and_handshake(
contract: Option<&str>, contract: Option<&str>,
args: &DevRequest, args: &DevRequest,
config: &Config, config: &Config,
) -> Result<String, Error> { ) -> Result<String> {
info!("Waiting for enclave start to deploy contract and handshake"); info!("Waiting for enclave start to deploy contract and handshake");
// Wait at most 60 seconds to connect to enclave // Wait at most 60 seconds to connect to enclave
@ -210,9 +204,7 @@ async fn deploy_and_handshake(
i -= 1; i -= 1;
if i == 0 { if i == 0 {
return Err(Error::GenericErr( return Err(eyre!("Could not connect to enclave"));
"Could not connect to enclave".to_string(),
));
} }
} }
// Calls which interact with enclave // Calls which interact with enclave
@ -231,34 +223,33 @@ async fn deploy_and_handshake(
contract_manifest: args.contract_manifest.clone(), contract_manifest: args.contract_manifest.clone(),
}; };
// Call handler // Call handler
let cd_res = contract_deploy.handle(config).await; let cd_res = contract_deploy
.handle(config)
.await
.wrap_err("Could not run `quartz contract deploy`")?;
// Return contract address or shutdown enclave & error if let Response::ContractDeploy(res) = cd_res {
match cd_res { res.contract_addr
Ok(Response::ContractDeploy(res)) => res.contract_addr, } else {
Err(e) => return Err(e), unreachable!("Unexpected response variant")
_ => unreachable!("Unexpected response variant"),
} }
}; };
// Run handshake // Run handshake
info!("Running handshake on contract `{}`", contract); info!("Running handshake on contract `{}`", contract);
let handshake = HandshakeRequest { let handshake = HandshakeRequest {
contract: wasmaddr_to_id(&contract).map_err(|_| Error::GenericErr(String::default()))?, contract: wasmaddr_to_id(&contract)?,
unsafe_trust_latest: args.unsafe_trust_latest, unsafe_trust_latest: args.unsafe_trust_latest,
}; };
let h_res = handshake.handle(config).await; let h_res = handshake
.handle(config)
match h_res { .await
Ok(Response::Handshake(res)) => { .wrap_err("Could not run `quartz handshake`")?;
info!("Handshake complete: {}", res.pub_key); println!("got here");
} if let Response::Handshake(res) = h_res {
Err(e) => { info!("Handshake complete: {}", res.pub_key);
return Err(e); }
}
_ => unreachable!("Unexpected response variant"),
};
Ok(contract) Ok(contract)
} }
@ -304,7 +295,7 @@ async fn watcher(tx: mpsc::Sender<DevRebuild>, log_dir: PathBuf) -> Result<()> {
wx.config.pathset([log_dir]); wx.config.pathset([log_dir]);
// Keep running until Watchexec quits // Keep running until Watchexec quits
let _ = main.await.into_diagnostic()?; let _ = main.await?;
Ok(()) Ok(())
} }

View file

@ -1,11 +1,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use color_eyre::owo_colors::OwoColorize; use color_eyre::{eyre::eyre, owo_colors::OwoColorize, Report, Result};
use tokio::process::Command; use tokio::process::Command;
use tracing::{debug, info}; use tracing::{debug, info};
use crate::{ use crate::{
config::Config, config::Config,
error::Error,
handler::Handler, handler::Handler,
request::enclave_build::EnclaveBuildRequest, request::enclave_build::EnclaveBuildRequest,
response::{enclave_build::EnclaveBuildResponse, Response}, response::{enclave_build::EnclaveBuildResponse, Response},
@ -13,13 +12,9 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for EnclaveBuildRequest { impl Handler for EnclaveBuildRequest {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref(); let config = config.as_ref();
info!("{}", "\nPeforming Enclave Build".blue().bold()); info!("{}", "\nPeforming Enclave Build".blue().bold());
@ -43,16 +38,10 @@ impl Handler for EnclaveBuildRequest {
} }
info!("{}", "🚧 Running build command ...".green().bold()); info!("{}", "🚧 Running build command ...".green().bold());
let status = command let status = command.status().await?;
.status()
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() { if !status.success() {
return Err(Error::GenericErr(format!( return Err(eyre!("Couldn't build enclave. {:?}", status));
"Couldn't build enclave. {:?}",
status
)));
} }
config.log_build(true).await?; config.log_build(true).await?;

View file

@ -2,7 +2,11 @@ use std::{fs, path::Path};
use async_trait::async_trait; use async_trait::async_trait;
use cargo_metadata::MetadataCommand; use cargo_metadata::MetadataCommand;
use color_eyre::owo_colors::OwoColorize; use color_eyre::{
eyre::{eyre, Context},
owo_colors::OwoColorize,
Report, Result,
};
use cosmrs::AccountId; use cosmrs::AccountId;
use quartz_common::enclave::types::Fmspc; use quartz_common::enclave::types::Fmspc;
use reqwest::Url; use reqwest::Url;
@ -12,7 +16,6 @@ use tracing::{debug, info};
use crate::{ use crate::{
config::Config, config::Config,
error::Error,
handler::{utils::helpers::write_cache_hash_height, Handler}, handler::{utils::helpers::write_cache_hash_height, Handler},
request::enclave_start::EnclaveStartRequest, request::enclave_start::EnclaveStartRequest,
response::{enclave_start::EnclaveStartResponse, Response}, response::{enclave_start::EnclaveStartResponse, Response},
@ -20,18 +23,16 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for EnclaveStartRequest { impl Handler for EnclaveStartRequest {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref().clone(); let config = config.as_ref().clone();
info!("{}", "\nPeforming Enclave Start".blue().bold()); info!("{}", "\nPeforming Enclave Start".blue().bold());
// Get trusted height and hash // Get trusted height and hash
let (trusted_height, trusted_hash) = self.get_hash_height(&config)?; let (trusted_height, trusted_hash) = self
.get_hash_height(&config)
.wrap_err("Error getting trusted hash and height")?;
write_cache_hash_height(trusted_height, trusted_hash, &config).await?; write_cache_hash_height(trusted_height, trusted_hash, &config).await?;
if config.mock_sgx { if config.mock_sgx {
@ -59,27 +60,21 @@ impl Handler for EnclaveStartRequest {
handle_process(enclave_child).await?; handle_process(enclave_child).await?;
} else { } else {
let Some(fmspc) = self.fmspc else { let Some(fmspc) = self.fmspc else {
return Err(Error::GenericErr( return Err(eyre!("FMSPC is required if MOCK_SGX isn't set"));
"FMSPC is required if MOCK_SGX isn't set".to_string(),
));
}; };
let Some(tcbinfo_contract) = self.tcbinfo_contract else { let Some(tcbinfo_contract) = self.tcbinfo_contract else {
return Err(Error::GenericErr( return Err(eyre!("tcbinfo_contract is required if MOCK_SGX isn't set"));
"tcbinfo_contract is required if MOCK_SGX isn't set".to_string(),
));
}; };
let Some(dcap_verifier_contract) = self.dcap_verifier_contract else { let Some(dcap_verifier_contract) = self.dcap_verifier_contract else {
return Err(Error::GenericErr( return Err(eyre!(
"dcap_verifier_contract is required if MOCK_SGX isn't set".to_string(), "dcap_verifier_contract is required if MOCK_SGX isn't set"
)); ));
}; };
if let Err(_) = std::env::var("ADMIN_SK") { if std::env::var("ADMIN_SK").is_err() {
return Err(Error::GenericErr( return Err(eyre!("ADMIN_SK environment variable is not set"));
"ADMIN_SK environment variable is not set".to_string(),
));
}; };
let enclave_dir = fs::canonicalize(config.app_dir.join("enclave"))?; let enclave_dir = fs::canonicalize(config.app_dir.join("enclave"))?;
@ -119,17 +114,11 @@ impl Handler for EnclaveStartRequest {
} }
} }
async fn handle_process(mut child: Child) -> Result<(), Error> { async fn handle_process(mut child: Child) -> Result<()> {
let status = child let status = child.wait().await?;
.wait()
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() { if !status.success() {
return Err(Error::GenericErr(format!( return Err(eyre!("Couldn't build enclave. {:?}", status));
"Couldn't build enclave. {:?}",
status
)));
} }
Ok(()) Ok(())
} }
@ -138,18 +127,17 @@ async fn create_mock_enclave_child(
app_dir: &Path, app_dir: &Path,
release: bool, release: bool,
enclave_args: Vec<String>, enclave_args: Vec<String>,
) -> Result<Child, Error> { ) -> Result<Child> {
let enclave_dir = app_dir.join("enclave"); let enclave_dir = app_dir.join("enclave");
let target_dir = app_dir.join("target"); let target_dir = app_dir.join("target");
// Use the enclave package metadata to get the path to the program binary // Use the enclave package metadata to get the path to the program binary
let package_name = MetadataCommand::new() let package_name = MetadataCommand::new()
.manifest_path(enclave_dir.join("Cargo.toml")) .manifest_path(enclave_dir.join("Cargo.toml"))
.exec() .exec()?
.map_err(|e| Error::GenericErr(e.to_string()))?
.root_package() .root_package()
.ok_or("No root package found in the metadata") .ok_or("No root package found in the metadata")
.map_err(|e| Error::GenericErr(e.to_string()))? .map_err(|e| eyre!(e))?
.name .name
.clone(); .clone();
@ -166,26 +154,18 @@ async fn create_mock_enclave_child(
debug!("Enclave Start Command: {:?}", command); debug!("Enclave Start Command: {:?}", command);
info!("{}", "🚧 Spawning enclave process ...".green().bold()); info!("{}", "🚧 Spawning enclave process ...".green().bold());
let child = command let child = command.kill_on_drop(true).spawn()?;
.kill_on_drop(true)
.spawn()
.map_err(|e| Error::GenericErr(e.to_string()))?;
Ok(child) Ok(child)
} }
async fn gramine_sgx_gen_private_key(enclave_dir: &Path) -> Result<(), Error> { async fn gramine_sgx_gen_private_key(enclave_dir: &Path) -> Result<()> {
// Launch the gramine-sgx-gen-private-key command // Launch the gramine-sgx-gen-private-key command
Command::new("gramine-sgx-gen-private-key") Command::new("gramine-sgx-gen-private-key")
.current_dir(enclave_dir) .current_dir(enclave_dir)
.output() .output()
.await .await
.map_err(|e| { .map_err(|e| eyre!("Failed to execute gramine-sgx-gen-private-key: {}", e))?;
Error::GenericErr(format!(
"Failed to execute gramine-sgx-gen-private-key: {}",
e
))
})?;
// Continue regardless of error // Continue regardless of error
// > /dev/null 2>&1 || : # may fail // > /dev/null 2>&1 || : # may fail
@ -205,7 +185,7 @@ async fn gramine_manifest(
node_url: &Url, node_url: &Url,
ws_url: &Url, ws_url: &Url,
grpc_url: &Url, grpc_url: &Url,
) -> Result<(), Error> { ) -> Result<()> {
let host = target_lexicon::HOST; let host = target_lexicon::HOST;
let arch_libdir = format!( let arch_libdir = format!(
"/lib/{}-{}-{}", "/lib/{}-{}-{}",
@ -214,7 +194,7 @@ async fn gramine_manifest(
let ra_client_spid = "51CAF5A48B450D624AEFE3286D314894"; let ra_client_spid = "51CAF5A48B450D624AEFE3286D314894";
let home_dir = dirs::home_dir() let home_dir = dirs::home_dir()
.ok_or(Error::GenericErr("home dir not set".to_string()))? .ok_or_else(|| eyre!("Home directory not set"))?
.display() .display()
.to_string(); .to_string();
@ -243,19 +223,19 @@ async fn gramine_manifest(
.current_dir(enclave_dir) .current_dir(enclave_dir)
.status() .status()
.await .await
.map_err(|e| Error::GenericErr(e.to_string()))?; .map_err(|e| eyre!("Failed to execute gramine-manifest: {}", e))?;
if !status.success() { if !status.success() {
return Err(Error::GenericErr(format!( return Err(eyre!(
"Couldn't run gramine manifest. {:?}", "gramine-manifest command failed with status: {:?}",
status status
))); ));
} }
Ok(()) Ok(())
} }
async fn gramine_sgx_sign(enclave_dir: &Path) -> Result<(), Error> { async fn gramine_sgx_sign(enclave_dir: &Path) -> Result<()> {
let status = Command::new("gramine-sgx-sign") let status = Command::new("gramine-sgx-sign")
.arg("--manifest") .arg("--manifest")
.arg("quartz.manifest") .arg("quartz.manifest")
@ -264,26 +244,27 @@ async fn gramine_sgx_sign(enclave_dir: &Path) -> Result<(), Error> {
.current_dir(enclave_dir) .current_dir(enclave_dir)
.status() .status()
.await .await
.map_err(|e| Error::GenericErr(e.to_string()))?; .map_err(|e| eyre!("Failed to execute gramine-sgx-sign: {}", e))?;
if !status.success() { if !status.success() {
return Err(Error::GenericErr(format!( return Err(eyre!(
"gramine-sgx-sign command failed. {:?}", "gramine-sgx-sign command failed with status: {:?}",
status status
))); ));
} }
Ok(()) Ok(())
} }
async fn create_gramine_sgx_child(enclave_dir: &Path) -> Result<Child, Error> { async fn create_gramine_sgx_child(enclave_dir: &Path) -> Result<Child> {
info!("🚧 Spawning enclave process ..."); info!("🚧 Spawning enclave process ...");
let child = Command::new("gramine-sgx") let child = Command::new("gramine-sgx")
.arg("./quartz") .arg("./quartz")
.kill_on_drop(true) .kill_on_drop(true)
.current_dir(enclave_dir) .current_dir(enclave_dir)
.spawn()?; .spawn()
.map_err(|e| eyre!("Failed to spawn gramine-sgx child process: {}", e))?;
Ok(child) Ok(child)
} }

View file

@ -1,6 +1,5 @@
use anyhow::anyhow;
use async_trait::async_trait; use async_trait::async_trait;
use color_eyre::owo_colors::OwoColorize; use color_eyre::{eyre::eyre, owo_colors::OwoColorize, Report, Result};
use cw_client::{CliClient, CwClient}; use cw_client::{CliClient, CwClient};
use futures_util::stream::StreamExt; use futures_util::stream::StreamExt;
use quartz_tm_prover::{config::Config as TmProverConfig, prover::prove}; use quartz_tm_prover::{config::Config as TmProverConfig, prover::prove};
@ -11,7 +10,6 @@ use tracing::{debug, info};
use super::utils::{helpers::block_tx_commit, types::WasmdTxResponse}; use super::utils::{helpers::block_tx_commit, types::WasmdTxResponse};
use crate::{ use crate::{
config::Config, config::Config,
error::Error,
handler::{ handler::{
utils::{helpers::read_cached_hash_height, relay::RelayMessage}, utils::{helpers::read_cached_hash_height, relay::RelayMessage},
Handler, Handler,
@ -22,27 +20,21 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for HandshakeRequest { impl Handler for HandshakeRequest {
type Error = Error;
type Response = Response; type Response = Response;
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref().clone(); let config = config.as_ref().clone();
info!("{}", "\nPeforming Handshake".blue().bold()); info!("{}", "\nPeforming Handshake".blue().bold());
// TODO: may need to import verbosity here // TODO: may need to import verbosity here
let pub_key = handshake(self, config) let pub_key = handshake(self, config).await?;
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
Ok(HandshakeResponse { pub_key }.into()) Ok(HandshakeResponse { pub_key }.into())
} }
} }
async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, anyhow::Error> { async fn handshake(args: HandshakeRequest, config: Config) -> Result<String> {
let tmrpc_client = HttpClient::new(config.node_url.as_str())?; let tmrpc_client = HttpClient::new(config.node_url.as_str())?;
let cw_client = CliClient::neutrond(config.node_url.clone()); let cw_client = CliClient::neutrond(config.node_url.clone());
@ -64,7 +56,8 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
json!(res), json!(res),
"11000untrn", "11000untrn",
) )
.await? .await
.map_err(|err| eyre!(Box::new(err)))?// TODO: change
.as_str(), .as_str(),
)?; )?;
debug!("\n\n SessionCreate tx output: {:?}", output); debug!("\n\n SessionCreate tx output: {:?}", output);
@ -92,7 +85,7 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
let proof_output = prove(prover_config) let proof_output = prove(prover_config)
.await .await
.map_err(|report| anyhow!("Tendermint prover failed. Report: {}", report))?; .map_err(|report| eyre!("Tendermint prover failed. Report: {}", report))?;
// Execute SessionSetPubKey on enclave // Execute SessionSetPubKey on enclave
info!("Running SessionSetPubKey"); info!("Running SessionSetPubKey");
@ -113,7 +106,8 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
json!(res), json!(res),
"11000untrn", "11000untrn",
) )
.await? .await
.map_err(|err| eyre!(Box::new(err)))? // todo change
.as_str(), .as_str(),
)?; )?;
@ -121,7 +115,9 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
block_tx_commit(&tmrpc_client, output.txhash).await?; block_tx_commit(&tmrpc_client, output.txhash).await?;
info!("SessionSetPubKey tx committed"); info!("SessionSetPubKey tx committed");
let output: WasmdTxResponse = cw_client.query_tx(&output.txhash.to_string())?; let output: WasmdTxResponse = cw_client
.query_tx(&output.txhash.to_string())
.map_err(|err| eyre!(Box::new(err)))?; // todo change
let wasm_event = output let wasm_event = output
.events .events
@ -136,11 +132,11 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
}) { }) {
Ok(pubkey.value_str()?.to_string()) Ok(pubkey.value_str()?.to_string())
} else { } else {
Err(anyhow!("Failed to find pubkey from SetPubKey message")) Err(eyre!("Failed to find pubkey from SetPubKey message"))
} }
} }
async fn two_block_waitoor(wsurl: &str) -> Result<(), anyhow::Error> { async fn two_block_waitoor(wsurl: &str) -> Result<()> {
let (client, driver) = WebSocketClient::new(wsurl).await?; let (client, driver) = WebSocketClient::new(wsurl).await?;
let driver_handle = tokio::spawn(async move { driver.run().await }); let driver_handle = tokio::spawn(async move { driver.run().await });

View file

@ -2,13 +2,12 @@ use std::path::PathBuf;
use async_trait::async_trait; use async_trait::async_trait;
use cargo_generate::{generate, GenerateArgs, TemplatePath, Vcs}; use cargo_generate::{generate, GenerateArgs, TemplatePath, Vcs};
use color_eyre::owo_colors::OwoColorize; use color_eyre::{eyre::Context, owo_colors::OwoColorize, Report, Result};
use tokio::fs; use tokio::fs;
use tracing::info; use tracing::info;
use crate::{ use crate::{
config::Config, config::Config,
error::Error,
handler::Handler, handler::Handler,
request::init::InitRequest, request::init::InitRequest,
response::{init::InitResponse, Response}, response::{init::InitResponse, Response},
@ -16,14 +15,10 @@ use crate::{
#[async_trait] #[async_trait]
impl Handler for InitRequest { impl Handler for InitRequest {
type Error = Error;
type Response = Response; type Response = Response;
// TODO: Add non-template init method // TODO: Add non-template init method
async fn handle<C: AsRef<Config> + Send>( async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
self,
config: C,
) -> Result<Self::Response, Self::Error> {
let config = config.as_ref(); let config = config.as_ref();
info!("{}", "\nPeforming Init".blue().bold()); info!("{}", "\nPeforming Init".blue().bold());
@ -36,7 +31,7 @@ impl Handler for InitRequest {
.expect("path already validated"); .expect("path already validated");
fs::create_dir_all(&parent) fs::create_dir_all(&parent)
.await .await
.map_err(|e| Error::GenericErr(e.to_string()))?; .wrap_err("Error creating directories to target app folder")?;
let file_name = self let file_name = self
.name .name

View file

@ -1,6 +1,9 @@
use std::time::Duration; use std::time::Duration;
use anyhow::anyhow; use color_eyre::{
eyre::{eyre, WrapErr},
Result,
};
use cosmrs::{AccountId, ErrorReport}; use cosmrs::{AccountId, ErrorReport};
use cw_client::{CliClient, CwClient}; use cw_client::{CliClient, CwClient};
use regex::Regex; use regex::Regex;
@ -13,15 +16,15 @@ use tendermint_rpc::{
use tokio::fs::{self}; use tokio::fs::{self};
use tracing::debug; use tracing::debug;
use crate::{config::Config, error::Error}; use crate::config::Config;
pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> { pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId> {
let _ = bech32_decode(address_str).map_err(|e| anyhow!(e))?; let _ = bech32_decode(address_str).map_err(|e| eyre!(e))?;
address_str.parse().map_err(|e: ErrorReport| anyhow!(e)) address_str.parse().map_err(|e: ErrorReport| eyre!(e))
} }
// Note: time until tx commit is empiraclly 800ms on DO wasmd chain. // Note: time until tx commit is empiraclly 800ms on DO wasmd chain.
pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxResponse, anyhow::Error> { pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxResponse> {
let re = Regex::new(r"tx \([A-F0-9]{64}\) not found")?; let re = Regex::new(r"tx \([A-F0-9]{64}\) not found")?;
tokio::time::sleep(Duration::from_millis(400)).await; tokio::time::sleep(Duration::from_millis(400)).await;
@ -35,7 +38,7 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
match e.0 { match e.0 {
ErrorDetail::Response(subdetail) => { ErrorDetail::Response(subdetail) => {
if !re.is_match(subdetail.source.data().unwrap_or_default()) { if !re.is_match(subdetail.source.data().unwrap_or_default()) {
return Err(anyhow!( return Err(eyre!(
"Error querying for tx: {}", "Error querying for tx: {}",
ErrorDetail::Response(subdetail) ErrorDetail::Response(subdetail)
)); ));
@ -46,7 +49,7 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
} }
} }
_ => { _ => {
return Err(anyhow!("Error querying for tx: {}", e.0)); return Err(eyre!("Error querying for tx: {}", e.0));
} }
} }
} }
@ -55,12 +58,13 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
} }
// Queries the chain for the latested height and hash // Queries the chain for the latested height and hash
pub fn query_latest_height_hash(node_url: Url) -> Result<(Height, Hash), Error> { pub fn query_latest_height_hash(node_url: Url) -> Result<(Height, Hash)> {
let cw_client = CliClient::neutrond(node_url); let cw_client = CliClient::neutrond(node_url);
let (trusted_height, trusted_hash) = cw_client let (trusted_height, trusted_hash) = cw_client
.trusted_height_hash() .trusted_height_hash()
.map_err(|e| Error::GenericErr(e.to_string()))?; .map_err(|e| eyre!(e))
.wrap_err("Could not query chain with cw client")?;
Ok(( Ok((
trusted_height.try_into()?, trusted_height.try_into()?,
@ -72,7 +76,7 @@ pub async fn write_cache_hash_height(
trusted_height: Height, trusted_height: Height,
trusted_hash: Hash, trusted_hash: Hash,
config: &Config, config: &Config,
) -> Result<(), Error> { ) -> Result<()> {
let height_path = config.cache_dir()?.join("trusted.height"); let height_path = config.cache_dir()?.join("trusted.height");
fs::write(height_path.as_path(), trusted_height.to_string()).await?; fs::write(height_path.as_path(), trusted_height.to_string()).await?;
let hash_path = config.cache_dir()?.join("trusted.hash"); let hash_path = config.cache_dir()?.join("trusted.hash");
@ -81,15 +85,21 @@ pub async fn write_cache_hash_height(
Ok(()) Ok(())
} }
pub async fn read_cached_hash_height(config: &Config) -> Result<(Height, Hash), Error> { pub async fn read_cached_hash_height(config: &Config) -> Result<(Height, Hash)> {
let height_path = config.cache_dir()?.join("trusted.height"); let height_path = config.cache_dir()?.join("trusted.height");
let hash_path = config.cache_dir()?.join("trusted.hash"); let hash_path = config.cache_dir()?.join("trusted.hash");
if !height_path.exists() { if !height_path.exists() {
return Err(Error::PathNotFile(height_path.display().to_string())); return Err(eyre!(
"Could not read trusted height from cache: {}",
height_path.display().to_string()
));
} }
if !hash_path.exists() { if !hash_path.exists() {
return Err(Error::PathNotFile(hash_path.display().to_string())); return Err(eyre!(
"Could not read trusted hash from cache: {}",
hash_path.display().to_string()
));
} }
let trusted_height: Height = fs::read_to_string(height_path.as_path()).await?.parse()?; let trusted_height: Height = fs::read_to_string(height_path.as_path()).await?.parse()?;

View file

@ -1,11 +1,10 @@
use color_eyre::{eyre::eyre, Result};
use quartz_common::proto::{ use quartz_common::proto::{
core_client::CoreClient, InstantiateRequest, SessionCreateRequest, SessionSetPubKeyRequest, core_client::CoreClient, InstantiateRequest, SessionCreateRequest, SessionSetPubKeyRequest,
}; };
use quartz_tm_prover::config::ProofOutput; use quartz_tm_prover::config::ProofOutput;
use serde_json::{json, Value as JsonValue}; use serde_json::{json, Value as JsonValue};
use crate::error::Error;
#[derive(Debug)] #[derive(Debug)]
pub enum RelayMessage { pub enum RelayMessage {
Instantiate { init_msg: JsonValue }, Instantiate { init_msg: JsonValue },
@ -14,17 +13,25 @@ pub enum RelayMessage {
} }
impl RelayMessage { impl RelayMessage {
pub async fn run_relay(self, enclave_rpc: String) -> Result<JsonValue, Error> { pub async fn run_relay(self, enclave_rpc: String) -> Result<JsonValue> {
// Query the gRPC quartz enclave service // Query the gRPC quartz enclave service
let mut qc_client = CoreClient::connect(enclave_rpc) let mut qc_client = CoreClient::connect(enclave_rpc).await.map_err(|e| {
.await eyre!(
.map_err(|e| Error::GenericErr(e.to_string()))?; "Failed to connect to the gRPC quartz enclave service: {}",
e
)
})?;
let attested_msg = match self { let attested_msg = match self {
RelayMessage::Instantiate { mut init_msg } => qc_client RelayMessage::Instantiate { mut init_msg } => qc_client
.instantiate(tonic::Request::new(InstantiateRequest {})) .instantiate(tonic::Request::new(InstantiateRequest {}))
.await .await
.map_err(|e| Error::GenericErr(e.to_string())) .map_err(|e| {
eyre!(
"Failed to instantiate via gRPC quartz enclave service: {}",
e
)
})
.map(|res| serde_json::from_str::<JsonValue>(&res.into_inner().message))? .map(|res| serde_json::from_str::<JsonValue>(&res.into_inner().message))?
.map(|msg| { .map(|msg| {
init_msg["quartz"] = msg; init_msg["quartz"] = msg;
@ -33,7 +40,12 @@ impl RelayMessage {
RelayMessage::SessionCreate => qc_client RelayMessage::SessionCreate => qc_client
.session_create(tonic::Request::new(SessionCreateRequest {})) .session_create(tonic::Request::new(SessionCreateRequest {}))
.await .await
.map_err(|e| Error::GenericErr(e.to_string())) .map_err(|e| {
eyre!(
"Failed to create session via gRPC quartz enclave service: {}",
e
)
})
.map(|res| serde_json::from_str::<JsonValue>(&res.into_inner().message))? .map(|res| serde_json::from_str::<JsonValue>(&res.into_inner().message))?
.map(|msg| json!({ "quartz": {"session_create": msg}}).to_string())?, .map(|msg| json!({ "quartz": {"session_create": msg}}).to_string())?,
RelayMessage::SessionSetPubKey { proof } => qc_client RelayMessage::SessionSetPubKey { proof } => qc_client
@ -41,7 +53,12 @@ impl RelayMessage {
message: serde_json::to_string(&proof)?, message: serde_json::to_string(&proof)?,
}) })
.await .await
.map_err(|e| Error::GenericErr(e.to_string())) .map_err(|e| {
eyre!(
"Failed to set public key via gRPC quartz enclave service: {}",
e
)
})
.map(|res| serde_json::from_str::<JsonValue>(&res.into_inner().message))? .map(|res| serde_json::from_str::<JsonValue>(&res.into_inner().message))?
.map(|msg| json!({ "quartz": {"session_set_pub_key": msg}}).to_string())?, .map(|msg| json!({ "quartz": {"session_set_pub_key": msg}}).to_string())?,
}; };

View file

@ -16,7 +16,6 @@
pub mod cache; pub mod cache;
pub mod cli; pub mod cli;
pub mod config; pub mod config;
pub mod error;
pub mod handler; pub mod handler;
pub mod request; pub mod request;
pub mod response; pub mod response;
@ -25,7 +24,10 @@ use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use cli::ToFigment; use cli::ToFigment;
use color_eyre::{eyre::Result, owo_colors::OwoColorize}; use color_eyre::{
eyre::{eyre, Result},
owo_colors::OwoColorize,
};
use config::Config; use config::Config;
use figment::{ use figment::{
providers::{Env, Format, Serialized, Toml}, providers::{Env, Format, Serialized, Toml},
@ -97,10 +99,10 @@ async fn main() -> Result<()> {
Ok(()) Ok(())
} }
fn check_path(path: &Option<PathBuf>) -> Result<(), error::Error> { fn check_path(path: &Option<PathBuf>) -> Result<()> {
if let Some(path) = path { if let Some(path) = path {
if !path.is_dir() { if !path.is_dir() {
return Err(error::Error::PathNotDir(format!("{}", path.display()))); return Err(eyre!("Path is not a directory: {}", path.display()));
} }
} }

View file

@ -1,6 +1,7 @@
use color_eyre::{eyre::eyre, Report, Result};
use crate::{ use crate::{
cli::{Command, ContractCommand, EnclaveCommand}, cli::{Command, ContractCommand, EnclaveCommand},
error::Error,
request::{ request::{
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest, contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
dev::DevRequest, enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest, dev::DevRequest, enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
@ -28,7 +29,7 @@ pub enum Request {
} }
impl TryFrom<Command> for Request { impl TryFrom<Command> for Request {
type Error = Error; type Error = Report;
fn try_from(cmd: Command) -> Result<Self, Self::Error> { fn try_from(cmd: Command) -> Result<Self, Self::Error> {
match cmd { match cmd {
@ -40,38 +41,46 @@ impl TryFrom<Command> for Request {
.into()), .into()),
Command::Contract { contract_command } => contract_command.try_into(), Command::Contract { contract_command } => contract_command.try_into(),
Command::Enclave { enclave_command } => enclave_command.try_into(), Command::Enclave { enclave_command } => enclave_command.try_into(),
Command::Dev(args) => Ok(DevRequest { Command::Dev(args) => {
watch: args.watch, if !args.contract_deploy.contract_manifest.exists() {
unsafe_trust_latest: args.unsafe_trust_latest, return Err(eyre!(
init_msg: serde_json::from_str(&args.contract_deploy.init_msg) "The contract manifest file does not exist: {}",
.map_err(|e| Error::GenericErr(e.to_string()))?, args.contract_deploy.contract_manifest.display()
label: args.contract_deploy.label, ));
contract_manifest: args.contract_deploy.contract_manifest, }
release: args.enclave_build.release,
fmspc: args.fmspc, Ok(DevRequest {
tcbinfo_contract: args.tcbinfo_contract, watch: args.watch,
dcap_verifier_contract: args.dcap_verifier_contract, unsafe_trust_latest: args.unsafe_trust_latest,
contract_manifest: args.contract_deploy.contract_manifest,
init_msg: serde_json::from_str(&args.contract_deploy.init_msg)?,
label: args.contract_deploy.label,
release: args.enclave_build.release,
fmspc: args.fmspc,
tcbinfo_contract: args.tcbinfo_contract,
dcap_verifier_contract: args.dcap_verifier_contract,
}
.into())
} }
.into()),
} }
} }
} }
impl TryFrom<ContractCommand> for Request { impl TryFrom<ContractCommand> for Request {
type Error = Error; type Error = Report;
fn try_from(cmd: ContractCommand) -> Result<Request, Error> { fn try_from(cmd: ContractCommand) -> Result<Request> {
match cmd { match cmd {
ContractCommand::Deploy(args) => { ContractCommand::Deploy(args) => {
if !args.contract_manifest.exists() { if !args.contract_manifest.exists() {
return Err(Error::PathNotFile( return Err(eyre!(
args.contract_manifest.display().to_string(), "The contract manifest file does not exist: {}",
args.contract_manifest.display()
)); ));
} }
Ok(ContractDeployRequest { Ok(ContractDeployRequest {
init_msg: serde_json::from_str(&args.init_msg) init_msg: serde_json::from_str(&args.init_msg)?,
.map_err(|e| Error::GenericErr(e.to_string()))?,
label: args.label, label: args.label,
contract_manifest: args.contract_manifest, contract_manifest: args.contract_manifest,
} }
@ -79,8 +88,9 @@ impl TryFrom<ContractCommand> for Request {
} }
ContractCommand::Build(args) => { ContractCommand::Build(args) => {
if !args.contract_manifest.exists() { if !args.contract_manifest.exists() {
return Err(Error::PathNotFile( return Err(eyre!(
args.contract_manifest.display().to_string(), "The contract manifest file does not exist: {}",
args.contract_manifest.display()
)); ));
} }
@ -94,9 +104,9 @@ impl TryFrom<ContractCommand> for Request {
} }
impl TryFrom<EnclaveCommand> for Request { impl TryFrom<EnclaveCommand> for Request {
type Error = Error; type Error = Report;
fn try_from(cmd: EnclaveCommand) -> Result<Request, Error> { fn try_from(cmd: EnclaveCommand) -> Result<Request> {
match cmd { match cmd {
EnclaveCommand::Build(_) => Ok(EnclaveBuildRequest {}.into()), EnclaveCommand::Build(_) => Ok(EnclaveBuildRequest {}.into()),
EnclaveCommand::Start(args) => Ok(EnclaveStartRequest { EnclaveCommand::Start(args) => Ok(EnclaveStartRequest {

View file

@ -1,8 +1,9 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use color_eyre::{eyre::Context, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{error::Error, request::Request}; use crate::request::Request;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ContractDeployRequest { pub struct ContractDeployRequest {
@ -18,10 +19,9 @@ impl From<ContractDeployRequest> for Request {
} }
impl ContractDeployRequest { impl ContractDeployRequest {
pub fn checked_init(init_msg: String) -> Result<GenericQuartzInit, Error> { pub fn checked_init(init_msg: String) -> Result<GenericQuartzInit> {
let parsed: GenericQuartzInit = serde_json::from_str(&init_msg).map_err(|_| { let parsed: GenericQuartzInit = serde_json::from_str(&init_msg)
Error::GenericErr("Init message doesn't contain mandatory quartz field.".to_string()) .wrap_err("Init message doesn't contain mandatory quartz field")?;
})?;
Ok(parsed) Ok(parsed)
} }

View file

@ -1,12 +1,10 @@
use color_eyre::Result;
use cosmrs::AccountId; use cosmrs::AccountId;
use quartz_common::enclave::types::Fmspc; use quartz_common::enclave::types::Fmspc;
use tendermint::{block::Height, Hash}; use tendermint::{block::Height, Hash};
use tracing::debug; use tracing::debug;
use crate::{ use crate::{config::Config, handler::utils::helpers::query_latest_height_hash, request::Request};
config::Config, error::Error, handler::utils::helpers::query_latest_height_hash,
request::Request,
};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EnclaveStartRequest { pub struct EnclaveStartRequest {
@ -24,7 +22,7 @@ impl From<EnclaveStartRequest> for Request {
impl EnclaveStartRequest { impl EnclaveStartRequest {
/// Returns the trusted hash and height /// Returns the trusted hash and height
pub fn get_hash_height(&self, config: &Config) -> Result<(Height, Hash), Error> { pub fn get_hash_height(&self, config: &Config) -> Result<(Height, Hash)> {
if self.unsafe_trust_latest || config.trusted_height == 0 || config.trusted_hash.is_empty() if self.unsafe_trust_latest || config.trusted_height == 0 || config.trusted_hash.is_empty()
{ {
debug!("querying latest trusted hash & height from node"); debug!("querying latest trusted hash & height from node");
@ -35,10 +33,7 @@ impl EnclaveStartRequest {
debug!("reusing config trusted hash & height"); debug!("reusing config trusted hash & height");
Ok(( Ok((
config.trusted_height.try_into()?, config.trusted_height.try_into()?,
config config.trusted_hash.parse()?,
.trusted_hash
.parse()
.map_err(|_| Error::GenericErr("invalid hash".to_string()))?,
)) ))
} }
} }

View file

@ -1,6 +1,8 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::{error::Error, request::Request}; use color_eyre::{eyre::eyre, Report, Result};
use crate::request::Request;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InitRequest { pub struct InitRequest {
@ -8,16 +10,16 @@ pub struct InitRequest {
} }
impl TryFrom<InitRequest> for Request { impl TryFrom<InitRequest> for Request {
type Error = Error; type Error = Report;
fn try_from(request: InitRequest) -> Result<Request, Error> { fn try_from(request: InitRequest) -> Result<Request> {
if request.name.extension().is_some() { if request.name.extension().is_some() {
return Err(Error::PathNotDir(format!("{}", request.name.display()))); return Err(eyre!("Path is not a directory: {}", request.name.display()));
} else if request.name.exists() { } else if request.name.exists() {
return Err(Error::GenericErr(format!( return Err(eyre!(
"Directory already exists: {}", "Directory already exists: {}",
request.name.display() request.name.display()
))); ));
} }
Ok(Request::Init(request)) Ok(Request::Init(request))

View file

@ -12,12 +12,13 @@ authors.workspace = true
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
anyhow.workspace = true
async-trait.workspace = true async-trait.workspace = true
color-eyre.workspace = true
hex.workspace = true hex.workspace = true
reqwest.workspace = true reqwest.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
anyhow.workspace = true
tonic.workspace = true tonic.workspace = true
cosmrs = { workspace = true, default-features = false, features = ["cosmwasm"] } cosmrs = { workspace = true, default-features = false, features = ["cosmwasm"] }

View file

@ -1,6 +1,6 @@
use std::process::Command; use std::process::Command;
use anyhow::anyhow; use color_eyre::{eyre::eyre, Help, Report, Result};
use cosmrs::{tendermint::chain::Id, AccountId}; use cosmrs::{tendermint::chain::Id, AccountId};
use reqwest::Url; use reqwest::Url;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
@ -55,8 +55,23 @@ impl CliClient {
} }
} }
fn new_command(&self) -> Command { fn new_command(&self) -> Result<Command> {
Command::new(self.kind.bin()) let bin = self.kind.bin();
if !self.is_bin_available(&bin) {
return Err(eyre!("Binary '{}' not found in PATH", bin)).suggestion(format!(
"Have you installed {}? If so, check that it's in your PATH.",
bin
));
}
Ok(Command::new(self.kind.bin()))
}
fn is_bin_available(&self, bin: &str) -> bool {
Command::new("which")
.arg(bin)
.output()
.map(|output| output.status.success())
.unwrap_or(false)
} }
} }
@ -66,14 +81,14 @@ impl CwClient for CliClient {
type Query = serde_json::Value; type Query = serde_json::Value;
type RawQuery = String; type RawQuery = String;
type ChainId = Id; type ChainId = Id;
type Error = anyhow::Error; type Error = Report;
async fn query_smart<R: DeserializeOwned + Send>( async fn query_smart<R: DeserializeOwned + Send>(
&self, &self,
contract: &Self::Address, contract: &Self::Address,
query: Self::Query, query: Self::Query,
) -> Result<R, Self::Error> { ) -> Result<R, Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command let command = command
.args(["--node", self.url.as_str()]) .args(["--node", self.url.as_str()])
.args(["query", "wasm"]) .args(["query", "wasm"])
@ -83,11 +98,11 @@ impl CwClient for CliClient {
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
let query_result: R = serde_json::from_slice(&output.stdout) let query_result: R = serde_json::from_slice(&output.stdout)
.map_err(|e| anyhow!("Error deserializing: {}", e))?; .map_err(|e| eyre!("Error deserializing: {}", e))?;
Ok(query_result) Ok(query_result)
} }
@ -96,7 +111,7 @@ impl CwClient for CliClient {
contract: &Self::Address, contract: &Self::Address,
query: Self::RawQuery, query: Self::RawQuery,
) -> Result<R, Self::Error> { ) -> Result<R, Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command let command = command
.args(["--node", self.url.as_str()]) .args(["--node", self.url.as_str()])
.args(["query", "wasm"]) .args(["query", "wasm"])
@ -106,7 +121,7 @@ impl CwClient for CliClient {
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default(); let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
@ -114,7 +129,7 @@ impl CwClient for CliClient {
} }
fn query_tx<R: DeserializeOwned + Default>(&self, txhash: &str) -> Result<R, Self::Error> { fn query_tx<R: DeserializeOwned + Default>(&self, txhash: &str) -> Result<R, Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command let command = command
.args(["--node", self.url.as_str()]) .args(["--node", self.url.as_str()])
.args(["query", "tx"]) .args(["query", "tx"])
@ -123,7 +138,7 @@ impl CwClient for CliClient {
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default(); let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
@ -139,7 +154,7 @@ impl CwClient for CliClient {
msg: M, msg: M,
fees: &str, fees: &str,
) -> Result<String, Self::Error> { ) -> Result<String, Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command let command = command
.args(["--node", self.url.as_str()]) .args(["--node", self.url.as_str()])
.args(["--chain-id", chain_id.as_ref()]) .args(["--chain-id", chain_id.as_ref()])
@ -154,7 +169,7 @@ impl CwClient for CliClient {
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
// TODO: find the rust type for the tx output and return that // TODO: find the rust type for the tx output and return that
@ -167,7 +182,7 @@ impl CwClient for CliClient {
sender: &str, sender: &str,
wasm_path: M, wasm_path: M,
) -> Result<String, Self::Error> { ) -> Result<String, Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command let command = command
.args(["--node", self.url.as_str()]) .args(["--node", self.url.as_str()])
.args(["tx", "wasm", "store", &wasm_path.to_string()]) .args(["tx", "wasm", "store", &wasm_path.to_string()])
@ -182,7 +197,7 @@ impl CwClient for CliClient {
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
// TODO: find the rust type for the tx output and return that // TODO: find the rust type for the tx output and return that
@ -197,7 +212,7 @@ impl CwClient for CliClient {
init_msg: M, init_msg: M,
label: &str, label: &str,
) -> Result<String, Self::Error> { ) -> Result<String, Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command let command = command
.args(["--node", self.url.as_str()]) .args(["--node", self.url.as_str()])
.args(["tx", "wasm", "instantiate"]) .args(["tx", "wasm", "instantiate"])
@ -215,7 +230,7 @@ impl CwClient for CliClient {
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
// TODO: find the rust type for the tx output and return that // TODO: find the rust type for the tx output and return that
@ -223,13 +238,13 @@ impl CwClient for CliClient {
} }
fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error> { fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error> {
let mut command = self.new_command(); let mut command = self.new_command()?;
let command = command.args(["--node", self.url.as_str()]).arg("status"); let command = command.args(["--node", self.url.as_str()]).arg("status");
let output = command.output()?; let output = command.output()?;
if !output.status.success() { if !output.status.success() {
return Err(anyhow!("{:?}", output)); return Err(eyre!("{:?}", output));
} }
let query_result: serde_json::Value = let query_result: serde_json::Value =
@ -241,13 +256,13 @@ impl CwClient for CliClient {
}; };
let trusted_height = query_result[sync_info]["latest_block_height"] let trusted_height = query_result[sync_info]["latest_block_height"]
.as_str() .as_str()
.ok_or(anyhow!("Could not query height"))?; .ok_or(eyre!("Could not query height"))?;
let trusted_height = trusted_height.parse::<u64>()?; let trusted_height = trusted_height.parse::<u64>()?;
let trusted_hash = query_result[sync_info]["latest_block_hash"] let trusted_hash = query_result[sync_info]["latest_block_hash"]
.as_str() .as_str()
.ok_or(anyhow!("Could not query height"))? .ok_or(eyre!("Could not query height"))?
.to_string(); .to_string();
Ok((trusted_height, trusted_hash)) Ok((trusted_height, trusted_hash))

View file

@ -950,6 +950,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"color-eyre",
"cosmos-sdk-proto", "cosmos-sdk-proto",
"cosmrs", "cosmrs",
"hex", "hex",

View file

@ -41,7 +41,8 @@ use crate::wslistener::WsListener;
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Cli::parse(); let args = Cli::parse();
let admin_sk = std::env::var("ADMIN_SK")?; let admin_sk = std::env::var("ADMIN_SK")
.map_err(|_| anyhow::anyhow!("Admin secret key not found in env vars"))?;
let light_client_opts = LightClientOpts::new( let light_client_opts = LightClientOpts::new(
args.chain_id.clone(), args.chain_id.clone(),