feat: move shared cli args to config file (#157)
This commit is contained in:
parent
f93da16c47
commit
72c7702719
23 changed files with 576 additions and 236 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -11,4 +11,5 @@ target/
|
||||||
artifacts/
|
artifacts/
|
||||||
.vscode/
|
.vscode/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
**/.env.local
|
**/.env.local
|
||||||
|
cli/quartz.toml
|
88
Cargo.lock
generated
88
Cargo.lock
generated
|
@ -382,6 +382,15 @@ dependencies = [
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -650,6 +659,12 @@ version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.16.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -1690,6 +1705,20 @@ version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "figment"
|
||||||
|
version = "0.10.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
|
||||||
|
dependencies = [
|
||||||
|
"atomic",
|
||||||
|
"pear",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
"uncased",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixedbitset"
|
name = "fixedbitset"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -2536,6 +2565,12 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inlinable_string"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -3309,6 +3344,29 @@ dependencies = [
|
||||||
"hmac",
|
"hmac",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pear"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467"
|
||||||
|
dependencies = [
|
||||||
|
"inlinable_string",
|
||||||
|
"pear_codegen",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pear_codegen"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"proc-macro2-diagnostics",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peg"
|
name = "peg"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
@ -3525,6 +3583,19 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2-diagnostics"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
"version_check",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prodash"
|
name = "prodash"
|
||||||
version = "28.0.0"
|
version = "28.0.0"
|
||||||
|
@ -3631,6 +3702,7 @@ dependencies = [
|
||||||
"cycles-sync",
|
"cycles-sync",
|
||||||
"dirs",
|
"dirs",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
|
"figment",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hex",
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
|
@ -3651,6 +3723,7 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tm-prover",
|
"tm-prover",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
"tonic 0.12.1",
|
"tonic 0.12.1",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
@ -5498,6 +5571,15 @@ version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uncased"
|
||||||
|
version = "0.9.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
|
@ -5950,6 +6032,12 @@ dependencies = [
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.35"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
[template]
|
[template]
|
||||||
name = "quartz-app-template"
|
|
||||||
description = "An functioning example of a quartz app"
|
description = "An functioning example of a quartz app"
|
||||||
|
|
||||||
ignore = [
|
ignore = [
|
||||||
|
@ -9,5 +8,7 @@ ignore = [
|
||||||
"enclave/target",
|
"enclave/target",
|
||||||
"enclave/target/*",
|
"enclave/target/*",
|
||||||
"frontend/package-lock.json",
|
"frontend/package-lock.json",
|
||||||
"frontend/public/images"
|
"frontend/public/images",
|
||||||
|
"trusted.hash",
|
||||||
|
"trusted.height"
|
||||||
]
|
]
|
8
apps/transfers/quartz.toml
Normal file
8
apps/transfers/quartz.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
mock_sgx = false
|
||||||
|
tx_sender = "admin"
|
||||||
|
chain_id = "testing"
|
||||||
|
node_url = "http://127.0.0.1:26657"
|
||||||
|
enclave_rpc_addr = "http://127.0.0.1"
|
||||||
|
enclave_rpc_port = 11090
|
||||||
|
trusted_hash = ""
|
||||||
|
trusted_height = 0
|
|
@ -33,6 +33,8 @@ base64 = "0.22.1"
|
||||||
subtle-encoding = "0.5.1"
|
subtle-encoding = "0.5.1"
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
target-lexicon = "0.12.16"
|
target-lexicon = "0.12.16"
|
||||||
|
regex = "1.10.5"
|
||||||
|
toml = "0.8.19"
|
||||||
|
|
||||||
# cosmos
|
# cosmos
|
||||||
cosmrs.workspace = true
|
cosmrs.workspace = true
|
||||||
|
@ -46,4 +48,4 @@ tm-prover = { workspace = true}
|
||||||
quartz-common = { workspace = true, features=["contract"]}
|
quartz-common = { workspace = true, features=["contract"]}
|
||||||
quartz-tee-ra = { workspace = true}
|
quartz-tee-ra = { workspace = true}
|
||||||
mtcs-enclave = { workspace = true, optional = false}
|
mtcs-enclave = { workspace = true, optional = false}
|
||||||
regex = "1.10.5"
|
figment = { version = "0.10.19", features=["env", "toml"] }
|
||||||
|
|
221
cli/src/cli.rs
221
cli/src/cli.rs
|
@ -1,12 +1,14 @@
|
||||||
use std::{env, path::PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
|
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
|
||||||
|
use figment::{providers::Serialized, Figment};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::metadata::LevelFilter;
|
use tracing::metadata::LevelFilter;
|
||||||
|
|
||||||
use crate::handler::utils::helpers::wasmaddr_to_id;
|
use crate::handler::utils::helpers::wasmaddr_to_id;
|
||||||
|
|
||||||
#[derive(clap::Args, Debug, Clone)]
|
#[derive(clap::Args, Debug, Clone, Serialize)]
|
||||||
pub struct Verbosity {
|
pub struct Verbosity {
|
||||||
/// Increase verbosity, can be repeated up to 2 times
|
/// Increase verbosity, can be repeated up to 2 times
|
||||||
#[arg(long, short, action = clap::ArgAction::Count)]
|
#[arg(long, short, action = clap::ArgAction::Count)]
|
||||||
|
@ -23,61 +25,45 @@ impl Verbosity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser, Serialize)]
|
||||||
#[command(version, long_about = None)]
|
#[command(version, long_about = None)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
/// Increase log verbosity
|
/// Increase log verbosity
|
||||||
#[clap(flatten)]
|
#[command(flatten)]
|
||||||
pub verbose: Verbosity,
|
pub verbose: Verbosity,
|
||||||
|
|
||||||
/// Enable mock SGX mode for testing purposes.
|
/// Enable mock SGX mode for testing purposes.
|
||||||
/// This flag disables the use of an Intel SGX processor and allows the system to run without remote attestations.
|
/// This flag disables the use of an Intel SGX processor and allows the system to run without remote attestations.
|
||||||
#[clap(long, env)]
|
#[arg(long)]
|
||||||
pub mock_sgx: bool,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mock_sgx: Option<bool>,
|
||||||
|
|
||||||
|
/// Path to Quartz app directory
|
||||||
|
/// Defaults to current working dir
|
||||||
|
/// For quartz init, root serves as the parent directory of the directory in which the quartz app is generated
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub app_dir: Option<PathBuf>,
|
||||||
|
|
||||||
/// Main command
|
/// Main command
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand, Serialize, Clone)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Create an empty Quartz app from a template
|
/// Create an empty Quartz app from a template
|
||||||
Init {
|
Init(InitArgs),
|
||||||
/// the name of your Quartz app directory, defaults to quartz_app
|
|
||||||
#[clap(long, default_value = "quartz_app")]
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
/// Perform handshake
|
/// Perform handshake
|
||||||
Handshake {
|
Handshake(HandshakeArgs),
|
||||||
/// path to create & init a Quartz app, defaults to current path if unspecified
|
|
||||||
#[arg(short, long, value_parser = wasmaddr_to_id)]
|
|
||||||
contract: AccountId,
|
|
||||||
/// Port enclave is listening on
|
|
||||||
#[arg(short, long, default_value = "11090")]
|
|
||||||
port: u16,
|
|
||||||
/// Name or address of private key with which to sign
|
|
||||||
#[arg(short, long, default_value = "admin")]
|
|
||||||
sender: String,
|
|
||||||
/// The network chain ID
|
|
||||||
#[arg(long, default_value = "testing")]
|
|
||||||
chain_id: ChainId,
|
|
||||||
/// <host>:<port> to tendermint rpc interface for this chain
|
|
||||||
#[clap(long, default_value_t = default_node_url())]
|
|
||||||
node_url: String,
|
|
||||||
/// RPC interface for the Quartz enclave
|
|
||||||
#[clap(long, default_value_t = default_rpc_addr())]
|
|
||||||
enclave_rpc_addr: String,
|
|
||||||
/// Path to Quartz app directory
|
|
||||||
/// Defaults to current working dir
|
|
||||||
#[clap(long)]
|
|
||||||
app_dir: Option<PathBuf>,
|
|
||||||
},
|
|
||||||
/// Subcommands for handling the Quartz app contract
|
/// Subcommands for handling the Quartz app contract
|
||||||
Contract {
|
Contract {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
contract_command: ContractCommand,
|
contract_command: ContractCommand,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Subcommands for handling the Quartz app enclave
|
/// Subcommands for handling the Quartz app enclave
|
||||||
Enclave {
|
Enclave {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
@ -85,58 +71,131 @@ pub enum Command {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Subcommand)]
|
#[derive(Debug, Clone, Subcommand, Serialize)]
|
||||||
pub enum ContractCommand {
|
pub enum ContractCommand {
|
||||||
Build {
|
Build(ContractBuildArgs),
|
||||||
#[clap(long)]
|
Deploy(ContractDeployArgs),
|
||||||
manifest_path: PathBuf,
|
|
||||||
},
|
|
||||||
Deploy {
|
|
||||||
/// Json-formatted cosmwasm contract initialization message
|
|
||||||
#[clap(long, default_value = "{}")]
|
|
||||||
init_msg: String,
|
|
||||||
/// <host>:<port> to tendermint rpc interface for this chain
|
|
||||||
#[clap(long, default_value_t = default_node_url())]
|
|
||||||
node_url: String,
|
|
||||||
/// Name or address of private key with which to sign
|
|
||||||
#[arg(short, long, default_value = "admin")]
|
|
||||||
sender: String,
|
|
||||||
/// The network chain ID
|
|
||||||
#[arg(long, default_value = "testing")]
|
|
||||||
chain_id: ChainId,
|
|
||||||
/// A human-readable name for this contract in lists
|
|
||||||
#[arg(long, default_value = "Quartz App Contract")]
|
|
||||||
label: String,
|
|
||||||
/// Path to contract wasm binary for deployment
|
|
||||||
#[clap(long)]
|
|
||||||
wasm_bin_path: PathBuf,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Subcommand)]
|
#[derive(Debug, Clone, Subcommand, Serialize)]
|
||||||
pub enum EnclaveCommand {
|
pub enum EnclaveCommand {
|
||||||
/// Build the Quartz app's enclave
|
/// Build the Quartz app's enclave
|
||||||
Build {
|
Build(EnclaveBuildArgs),
|
||||||
/// Path to Cargo.toml file of the Quartz app's enclave package, defaults to './enclave/Cargo.toml' if unspecified
|
/// Run the Quartz app's enclave
|
||||||
#[arg(long, default_value = "./enclave/Cargo.toml")]
|
Start(EnclaveStartArgs),
|
||||||
manifest_path: PathBuf,
|
|
||||||
},
|
|
||||||
// Run the Quartz app's enclave
|
|
||||||
Start {
|
|
||||||
/// Path to quartz app directory
|
|
||||||
/// Defaults to current working dir
|
|
||||||
#[clap(long)]
|
|
||||||
app_dir: Option<PathBuf>,
|
|
||||||
/// The network chain ID
|
|
||||||
#[clap(long)]
|
|
||||||
chain_id: String,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_rpc_addr() -> String {
|
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||||
env::var("RPC_URL").unwrap_or_else(|_| "http://127.0.0.1".to_string())
|
pub struct InitArgs {
|
||||||
|
/// The name of your Quartz app directory, defaults to quartz_app
|
||||||
|
#[arg(default_value = "quartz_app")]
|
||||||
|
pub name: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_node_url() -> String {
|
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||||
env::var("NODE_URL").unwrap_or_else(|_| "http://127.0.0.1:26657".to_string())
|
pub struct HandshakeArgs {
|
||||||
|
/// Path to create & init a Quartz app, defaults to current path if unspecified
|
||||||
|
#[arg(short, long, value_parser = wasmaddr_to_id)]
|
||||||
|
pub contract: AccountId,
|
||||||
|
|
||||||
|
/// Name or address of private key with which to sign
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tx_sender: Option<String>,
|
||||||
|
|
||||||
|
/// The network chain ID
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub chain_id: Option<ChainId>,
|
||||||
|
|
||||||
|
/// <host>:<port> to tendermint rpc interface for this chain
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub node_url: Option<String>,
|
||||||
|
|
||||||
|
/// RPC interface for the Quartz enclave
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enclave_rpc_addr: Option<String>,
|
||||||
|
|
||||||
|
/// Port enclave is listening on
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
||||||
|
pub enclave_rpc_port: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ContractBuildArgs {
|
||||||
|
#[arg(long)]
|
||||||
|
pub manifest_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ContractDeployArgs {
|
||||||
|
/// Json-formatted cosmwasm contract initialization message
|
||||||
|
#[arg(long, default_value = "{}")]
|
||||||
|
pub init_msg: String,
|
||||||
|
|
||||||
|
/// <host>:<port> to tendermint rpc interface for this chain
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub node_url: Option<String>,
|
||||||
|
|
||||||
|
/// Name or address of private key with which to sign
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tx_sender: Option<String>,
|
||||||
|
|
||||||
|
/// The network chain ID
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub chain_id: Option<ChainId>,
|
||||||
|
|
||||||
|
/// A human-readable name for this contract in lists
|
||||||
|
#[arg(long, default_value = "Quartz App Contract")]
|
||||||
|
pub label: String,
|
||||||
|
|
||||||
|
/// Path to contract wasm binary for deployment
|
||||||
|
#[arg(long)]
|
||||||
|
pub wasm_bin_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct EnclaveBuildArgs {
|
||||||
|
/// Path to Cargo.toml file of the Quartz app's enclave package, defaults to './enclave/Cargo.toml' if unspecified
|
||||||
|
#[arg(long, default_value = "./enclave/Cargo.toml")]
|
||||||
|
pub manifest_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct EnclaveStartArgs {
|
||||||
|
/// The network chain ID
|
||||||
|
#[arg(long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub chain_id: Option<ChainId>,
|
||||||
|
|
||||||
|
/// Fetch latest trusted hash and height from the chain instead of existing configuration
|
||||||
|
#[arg(long)]
|
||||||
|
pub use_latest_trusted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToFigment {
|
||||||
|
fn to_figment(&self) -> Figment;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToFigment for Command {
|
||||||
|
fn to_figment(&self) -> Figment {
|
||||||
|
match self {
|
||||||
|
Command::Init(args) => Figment::from(Serialized::defaults(args)),
|
||||||
|
Command::Handshake(args) => Figment::from(Serialized::defaults(args)),
|
||||||
|
Command::Contract { contract_command } => match contract_command {
|
||||||
|
ContractCommand::Build(args) => Figment::from(Serialized::defaults(args)),
|
||||||
|
ContractCommand::Deploy(args) => Figment::from(Serialized::defaults(args)),
|
||||||
|
},
|
||||||
|
Command::Enclave { enclave_command } => match enclave_command {
|
||||||
|
EnclaveCommand::Build(args) => Figment::from(Serialized::defaults(args)),
|
||||||
|
EnclaveCommand::Start(args) => Figment::from(Serialized::defaults(args)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
85
cli/src/config.rs
Normal file
85
cli/src/config.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
use std::{env, path::PathBuf};
|
||||||
|
|
||||||
|
use cosmrs::tendermint::chain::Id as ChainId;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct Config {
|
||||||
|
/// Enable mock SGX mode for testing purposes.
|
||||||
|
/// This flag disables the use of an Intel SGX processor and allows the system to run without remote attestations.
|
||||||
|
#[serde(default)]
|
||||||
|
pub mock_sgx: bool,
|
||||||
|
|
||||||
|
/// Name or address of private key with which to sign
|
||||||
|
#[serde(default = "default_tx_sender")]
|
||||||
|
pub tx_sender: String,
|
||||||
|
|
||||||
|
/// The network chain ID
|
||||||
|
#[serde(default = "default_chain_id")]
|
||||||
|
pub chain_id: ChainId,
|
||||||
|
|
||||||
|
/// <host>:<port> to tendermint rpc interface for this chain
|
||||||
|
#[serde(default = "default_node_url")]
|
||||||
|
pub node_url: String,
|
||||||
|
|
||||||
|
/// RPC interface for the Quartz enclave
|
||||||
|
#[serde(default = "default_rpc_addr")]
|
||||||
|
pub enclave_rpc_addr: String,
|
||||||
|
|
||||||
|
/// Port enclave is listening on
|
||||||
|
#[serde(default = "default_port")]
|
||||||
|
pub enclave_rpc_port: u16,
|
||||||
|
|
||||||
|
/// Path to Quartz app directory
|
||||||
|
/// Defaults to current working dir
|
||||||
|
#[serde(default = "default_app_dir")]
|
||||||
|
pub app_dir: PathBuf,
|
||||||
|
|
||||||
|
/// Trusted height for light client proofs
|
||||||
|
#[serde(default)]
|
||||||
|
pub trusted_height: u64,
|
||||||
|
|
||||||
|
/// Trusted hash for block at trusted_height for light client proofs
|
||||||
|
#[serde(default)]
|
||||||
|
pub trusted_hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_rpc_addr() -> String {
|
||||||
|
env::var("RPC_URL").unwrap_or_else(|_| "http://127.0.0.1".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_node_url() -> String {
|
||||||
|
env::var("NODE_URL").unwrap_or_else(|_| "http://127.0.0.1:26657".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_tx_sender() -> String {
|
||||||
|
String::from("admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_chain_id() -> ChainId {
|
||||||
|
"testing".parse().expect("default chain_id failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_port() -> u16 {
|
||||||
|
11090
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_app_dir() -> PathBuf {
|
||||||
|
".".parse().expect("default app_dir pathbuf failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config {
|
||||||
|
mock_sgx: false,
|
||||||
|
tx_sender: default_tx_sender(),
|
||||||
|
chain_id: default_chain_id(),
|
||||||
|
node_url: default_node_url(),
|
||||||
|
enclave_rpc_addr: default_rpc_addr(),
|
||||||
|
enclave_rpc_port: default_port(),
|
||||||
|
app_dir: default_app_dir(),
|
||||||
|
trusted_height: u64::default(),
|
||||||
|
trusted_hash: String::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,4 +9,34 @@ pub enum Error {
|
||||||
PathNotFile(String),
|
PathNotFile(String),
|
||||||
/// unspecified error: {0}
|
/// unspecified error: {0}
|
||||||
GenericErr(String),
|
GenericErr(String),
|
||||||
|
/// IoError: {0}
|
||||||
|
IoError(String),
|
||||||
|
/// TOML Error : {0}
|
||||||
|
TomlError(String),
|
||||||
|
/// Tendermint error: {0}
|
||||||
|
TendermintError(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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use crate::{error::Error, request::Request, response::Response, Config};
|
use crate::{config::Config, error::Error, request::Request, response::Response};
|
||||||
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
// commands
|
// commands
|
||||||
|
|
|
@ -4,11 +4,11 @@ use async_trait::async_trait;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
handler::Handler,
|
handler::Handler,
|
||||||
request::contract_build::ContractBuildRequest,
|
request::contract_build::ContractBuildRequest,
|
||||||
response::{contract_build::ContractBuildResponse, Response},
|
response::{contract_build::ContractBuildResponse, Response},
|
||||||
Config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
|
@ -17,11 +17,11 @@ use super::utils::{
|
||||||
types::{Log, WasmdTxResponse},
|
types::{Log, WasmdTxResponse},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
handler::{utils::types::RelayMessage, Handler},
|
handler::{utils::types::RelayMessage, Handler},
|
||||||
request::contract_deploy::ContractDeployRequest,
|
request::contract_deploy::ContractDeployRequest,
|
||||||
response::{contract_deploy::ContractDeployResponse, Response},
|
response::{contract_deploy::ContractDeployResponse, Response},
|
||||||
Config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -33,11 +33,11 @@ impl Handler for ContractDeployRequest {
|
||||||
trace!("initializing directory structure...");
|
trace!("initializing directory structure...");
|
||||||
|
|
||||||
let (code_id, contract_addr) = if config.mock_sgx {
|
let (code_id, contract_addr) = if config.mock_sgx {
|
||||||
deploy::<RawMockAttestation>(self, config.mock_sgx)
|
deploy::<RawMockAttestation>(self, config)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||||
} else {
|
} else {
|
||||||
deploy::<RawEpidAttestation>(self, config.mock_sgx)
|
deploy::<RawEpidAttestation>(self, config)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::GenericErr(e.to_string()))?
|
.map_err(|e| Error::GenericErr(e.to_string()))?
|
||||||
};
|
};
|
||||||
|
@ -52,12 +52,12 @@ impl Handler for ContractDeployRequest {
|
||||||
|
|
||||||
async fn deploy<DA: Serialize + DeserializeOwned>(
|
async fn deploy<DA: Serialize + DeserializeOwned>(
|
||||||
args: ContractDeployRequest,
|
args: ContractDeployRequest,
|
||||||
mock_sgx: bool,
|
config: Config,
|
||||||
) -> Result<(u64, String), anyhow::Error> {
|
) -> Result<(u64, String), anyhow::Error> {
|
||||||
// TODO: Replace with call to Rust package
|
// TODO: Replace with call to Rust package
|
||||||
let relay_path = current_dir()?.join("../");
|
let relay_path = current_dir()?.join("../");
|
||||||
|
|
||||||
let httpurl = Url::parse(&format!("http://{}", args.node_url))?;
|
let httpurl = Url::parse(&format!("http://{}", config.node_url))?;
|
||||||
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
|
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
|
||||||
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
||||||
|
|
||||||
|
@ -67,8 +67,8 @@ async fn deploy<DA: Serialize + DeserializeOwned>(
|
||||||
|
|
||||||
// TODO: uncertain about the path -> string conversion
|
// TODO: uncertain about the path -> string conversion
|
||||||
let deploy_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.deploy(
|
let deploy_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.deploy(
|
||||||
&args.chain_id,
|
&config.chain_id,
|
||||||
&args.sender,
|
&config.tx_sender,
|
||||||
contract_path.display().to_string(),
|
contract_path.display().to_string(),
|
||||||
)?)?;
|
)?)?;
|
||||||
let res = block_tx_commit(&tmrpc_client, deploy_output.txhash).await?;
|
let res = block_tx_commit(&tmrpc_client, deploy_output.txhash).await?;
|
||||||
|
@ -79,17 +79,18 @@ async fn deploy<DA: Serialize + DeserializeOwned>(
|
||||||
info!("\n🚀 Communicating with Relay to Instantiate...\n");
|
info!("\n🚀 Communicating with Relay to Instantiate...\n");
|
||||||
let raw_init_msg = run_relay::<QuartzInstantiateMsg<DA>>(
|
let raw_init_msg = run_relay::<QuartzInstantiateMsg<DA>>(
|
||||||
relay_path.as_path(),
|
relay_path.as_path(),
|
||||||
mock_sgx,
|
config.mock_sgx,
|
||||||
RelayMessage::Instantiate,
|
RelayMessage::Instantiate,
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
info!("\n🚀 Instantiating {} Contract\n", args.label);
|
info!("\n🚀 Instantiating {} Contract\n", args.label);
|
||||||
let mut init_msg = args.init_msg;
|
let mut init_msg = args.init_msg;
|
||||||
init_msg["quartz"] = json!(raw_init_msg);
|
init_msg["quartz"] = json!(raw_init_msg);
|
||||||
|
|
||||||
let init_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.init(
|
let init_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.init(
|
||||||
&args.chain_id,
|
&config.chain_id,
|
||||||
&args.sender,
|
&config.tx_sender,
|
||||||
code_id,
|
code_id,
|
||||||
json!(init_msg),
|
json!(init_msg),
|
||||||
&format!("{} Contract #{}", args.label, code_id),
|
&format!("{} Contract #{}", args.label, code_id),
|
||||||
|
|
|
@ -4,11 +4,11 @@ use async_trait::async_trait;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
handler::Handler,
|
handler::Handler,
|
||||||
request::enclave_build::EnclaveBuildRequest,
|
request::enclave_build::EnclaveBuildRequest,
|
||||||
response::{enclave_build::EnclaveBuildResponse, Response},
|
response::{enclave_build::EnclaveBuildResponse, Response},
|
||||||
Config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
|
@ -4,13 +4,12 @@ use async_trait::async_trait;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use super::utils::helpers::read_hash_height;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
handler::Handler,
|
handler::{utils::helpers::get_hash_height, Handler},
|
||||||
request::enclave_start::EnclaveStartRequest,
|
request::enclave_start::EnclaveStartRequest,
|
||||||
response::{enclave_start::EnclaveStartResponse, Response},
|
response::{enclave_start::EnclaveStartResponse, Response},
|
||||||
Config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -18,16 +17,16 @@ impl Handler for EnclaveStartRequest {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Response = Response;
|
type Response = Response;
|
||||||
|
|
||||||
async fn handle(self, config: Config) -> Result<Self::Response, Self::Error> {
|
async fn handle(self, mut config: Config) -> Result<Self::Response, Self::Error> {
|
||||||
let enclave_dir = self.app_dir.join("enclave");
|
// Get trusted height and hash
|
||||||
let (trusted_height, trusted_hash) = read_hash_height(self.app_dir.as_path())
|
let (trusted_height, trusted_hash) = get_hash_height(self.use_latest_trusted, &mut config)?;
|
||||||
.await
|
|
||||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
let enclave_dir = config.app_dir.join("enclave");
|
||||||
|
|
||||||
if config.mock_sgx {
|
if config.mock_sgx {
|
||||||
let enclave_args: Vec<String> = vec![
|
let enclave_args: Vec<String> = vec![
|
||||||
"--chain-id".to_string(),
|
"--chain-id".to_string(),
|
||||||
self.chain_id,
|
config.chain_id.to_string(),
|
||||||
"--trusted-height".to_string(),
|
"--trusted-height".to_string(),
|
||||||
trusted_height.to_string(),
|
trusted_height.to_string(),
|
||||||
"--trusted-hash".to_string(),
|
"--trusted-hash".to_string(),
|
||||||
|
|
|
@ -17,14 +17,14 @@ use super::utils::{
|
||||||
types::WasmdTxResponse,
|
types::WasmdTxResponse,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
handler::{
|
handler::{
|
||||||
utils::{helpers::read_hash_height, types::RelayMessage},
|
utils::{helpers::get_hash_height, types::RelayMessage},
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
request::handshake::HandshakeRequest,
|
request::handshake::HandshakeRequest,
|
||||||
response::{handshake::HandshakeResponse, Response},
|
response::{handshake::HandshakeResponse, Response},
|
||||||
Config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -36,7 +36,7 @@ impl Handler for HandshakeRequest {
|
||||||
trace!("starting handshake...");
|
trace!("starting handshake...");
|
||||||
|
|
||||||
// TODO: may need to import verbosity here
|
// TODO: may need to import verbosity here
|
||||||
let pub_key = handshake(self, config.mock_sgx)
|
let pub_key = handshake(self, config)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||||
|
|
||||||
|
@ -49,30 +49,32 @@ struct Message<'a> {
|
||||||
message: &'a str,
|
message: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, anyhow::Error> {
|
async fn handshake(args: HandshakeRequest, mut config: Config) -> Result<String, anyhow::Error> {
|
||||||
let httpurl = Url::parse(&format!("http://{}", args.node_url))?;
|
let httpurl = Url::parse(&format!("http://{}", config.node_url))?;
|
||||||
let wsurl = format!("ws://{}/websocket", args.node_url);
|
let wsurl = format!("ws://{}/websocket", config.node_url);
|
||||||
|
|
||||||
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
|
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
|
||||||
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
|
||||||
|
|
||||||
|
let (trusted_height, trusted_hash) = get_hash_height(false, &mut config)?;
|
||||||
// TODO: dir logic issue #125
|
// TODO: dir logic issue #125
|
||||||
// Read trusted hash and height from files
|
|
||||||
let base_path = current_dir()?.join("../");
|
let base_path = current_dir()?.join("../");
|
||||||
let trusted_files_path = args.app_dir;
|
|
||||||
let (trusted_height, trusted_hash) = read_hash_height(trusted_files_path.as_path()).await?;
|
|
||||||
|
|
||||||
info!("Running SessionCreate");
|
info!("Running SessionCreate");
|
||||||
let res: serde_json::Value =
|
let res: serde_json::Value = run_relay(
|
||||||
run_relay(base_path.as_path(), mock_sgx, RelayMessage::SessionCreate)?;
|
base_path.as_path(),
|
||||||
|
config.mock_sgx,
|
||||||
|
RelayMessage::SessionCreate,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let output: WasmdTxResponse = serde_json::from_str(
|
let output: WasmdTxResponse = serde_json::from_str(
|
||||||
wasmd_client
|
wasmd_client
|
||||||
.tx_execute(
|
.tx_execute(
|
||||||
&args.contract.clone(),
|
&args.contract.clone(),
|
||||||
&args.chain_id,
|
&config.chain_id,
|
||||||
2000000,
|
2000000,
|
||||||
&args.sender,
|
&config.tx_sender,
|
||||||
json!(res),
|
json!(res),
|
||||||
)?
|
)?
|
||||||
.as_str(),
|
.as_str(),
|
||||||
|
@ -92,7 +94,7 @@ async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, any
|
||||||
debug!("Proof path: {:?}", proof_path.to_str());
|
debug!("Proof path: {:?}", proof_path.to_str());
|
||||||
|
|
||||||
// Call tm prover with trusted hash and height
|
// Call tm prover with trusted hash and height
|
||||||
let config = TmProverConfig {
|
let prover_config = TmProverConfig {
|
||||||
primary: httpurl.as_str().parse()?,
|
primary: httpurl.as_str().parse()?,
|
||||||
witnesses: httpurl.as_str().parse()?,
|
witnesses: httpurl.as_str().parse()?,
|
||||||
trusted_height,
|
trusted_height,
|
||||||
|
@ -101,11 +103,11 @@ async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, any
|
||||||
verbose: "1".parse()?, // TODO: both tm-prover and cli define the same Verbosity struct. Need to define this once and import
|
verbose: "1".parse()?, // TODO: both tm-prover and cli define the same Verbosity struct. Need to define this once and import
|
||||||
contract_address: args.contract.clone(),
|
contract_address: args.contract.clone(),
|
||||||
storage_key: "quartz_session".to_string(),
|
storage_key: "quartz_session".to_string(),
|
||||||
chain_id: args.chain_id.to_string(),
|
chain_id: config.chain_id.to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
debug!("config: {:?}", config);
|
debug!("config: {:?}", prover_config);
|
||||||
if let Err(report) = prove(config).await {
|
if let Err(report) = prove(prover_config).await {
|
||||||
return Err(anyhow!("Tendermint prover failed. Report: {}", report));
|
return Err(anyhow!("Tendermint prover failed. Report: {}", report));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,9 +121,10 @@ async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, any
|
||||||
info!("Running SessionSetPubKey");
|
info!("Running SessionSetPubKey");
|
||||||
let res: serde_json::Value = run_relay(
|
let res: serde_json::Value = run_relay(
|
||||||
base_path.as_path(),
|
base_path.as_path(),
|
||||||
mock_sgx,
|
config.mock_sgx,
|
||||||
RelayMessage::SessionSetPubKey(proof_json),
|
RelayMessage::SessionSetPubKey(proof_json),
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Submit SessionSetPubKey to contract
|
// Submit SessionSetPubKey to contract
|
||||||
let output: WasmdTxResponse = serde_json::from_str(
|
let output: WasmdTxResponse = serde_json::from_str(
|
||||||
|
@ -130,7 +133,7 @@ async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, any
|
||||||
&args.contract.clone(),
|
&args.contract.clone(),
|
||||||
&ChainId::from_str("testing")?,
|
&ChainId::from_str("testing")?,
|
||||||
2000000,
|
2000000,
|
||||||
&args.sender,
|
&config.tx_sender,
|
||||||
json!(res),
|
json!(res),
|
||||||
)?
|
)?
|
||||||
.as_str(),
|
.as_str(),
|
||||||
|
|
|
@ -2,14 +2,15 @@ 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 tokio::fs;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::Config,
|
||||||
error::Error,
|
error::Error,
|
||||||
handler::Handler,
|
handler::Handler,
|
||||||
request::init::InitRequest,
|
request::init::InitRequest,
|
||||||
response::{init::InitResponse, Response},
|
response::{init::InitResponse, Response},
|
||||||
Config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -17,13 +18,30 @@ impl Handler for InitRequest {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Response = Response;
|
type Response = Response;
|
||||||
|
|
||||||
async fn handle(self, _config: Config) -> Result<Self::Response, Self::Error> {
|
async fn handle(self, config: Config) -> Result<Self::Response, Self::Error> {
|
||||||
trace!("initializing directory structure...");
|
trace!("initializing directory structure...");
|
||||||
|
|
||||||
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
|
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
|
||||||
|
|
||||||
|
let parent = self
|
||||||
|
.name
|
||||||
|
.parent()
|
||||||
|
.map(|p| p.to_path_buf())
|
||||||
|
.expect("path already validated");
|
||||||
|
fs::create_dir_all(&parent)
|
||||||
|
.await
|
||||||
|
.map_err(|e| Error::GenericErr(e.to_string()))?;
|
||||||
|
|
||||||
|
let file_name = self
|
||||||
|
.name
|
||||||
|
.file_name()
|
||||||
|
.and_then(|f| f.to_str())
|
||||||
|
.expect("path already validated");
|
||||||
|
|
||||||
let wasm_pack_args = GenerateArgs {
|
let wasm_pack_args = GenerateArgs {
|
||||||
name: Some(self.name),
|
name: Some(file_name.to_string()),
|
||||||
|
destination: Some(config.app_dir.join(parent)),
|
||||||
|
overwrite: true,
|
||||||
vcs: Some(Vcs::Git),
|
vcs: Some(Vcs::Git),
|
||||||
template_path: TemplatePath {
|
template_path: TemplatePath {
|
||||||
// git: Some("git@github.com:informalsystems/cycles-quartz.git".to_string()), // TODO: replace with public http address when open-sourced
|
// git: Some("git@github.com:informalsystems/cycles-quartz.git".to_string()), // TODO: replace with public http address when open-sourced
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
use std::{path::Path, process::Command, time::Duration};
|
use std::{path::Path, time::Duration};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use cosmrs::{AccountId, ErrorReport};
|
use cosmrs::{AccountId, ErrorReport};
|
||||||
|
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use reqwest::Url;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use subtle_encoding::bech32::decode as bech32_decode;
|
use subtle_encoding::bech32::decode as bech32_decode;
|
||||||
use tendermint::{block::Height, Hash};
|
use tendermint::{block::Height, Hash};
|
||||||
use tendermint_rpc::{
|
use tendermint_rpc::{
|
||||||
endpoint::tx::Response as TmTxResponse, error::ErrorDetail, Client, HttpClient,
|
endpoint::tx::Response as TmTxResponse, error::ErrorDetail, Client, HttpClient,
|
||||||
};
|
};
|
||||||
use tokio::fs;
|
use tokio::{fs, process::Command};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use super::types::RelayMessage;
|
use super::types::RelayMessage;
|
||||||
|
use crate::{config::Config, error};
|
||||||
|
|
||||||
pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> {
|
pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> {
|
||||||
let (hr, _) = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
|
let (hr, _) = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
|
||||||
|
@ -24,7 +27,7 @@ pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move wrapping result with "quartz:" struct into here
|
// TODO: move wrapping result with "quartz:" struct into here
|
||||||
pub fn run_relay<R: DeserializeOwned>(
|
pub async fn run_relay<R: DeserializeOwned>(
|
||||||
base_path: &Path,
|
base_path: &Path,
|
||||||
mock_sgx: bool,
|
mock_sgx: bool,
|
||||||
msg: RelayMessage,
|
msg: RelayMessage,
|
||||||
|
@ -41,7 +44,7 @@ pub fn run_relay<R: DeserializeOwned>(
|
||||||
command.arg(proof);
|
command.arg(proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = command.output()?;
|
let output = command.output().await?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow!("{:?}", output));
|
return Err(anyhow!("{:?}", output));
|
||||||
|
@ -87,18 +90,55 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_hash_height(base_path: &Path) -> Result<(Height, Hash), anyhow::Error> {
|
/// Returns the trusted hash and height
|
||||||
let height_path = base_path.join("trusted.height");
|
pub fn get_hash_height(
|
||||||
let trusted_height: Height = fs::read_to_string(height_path.as_path())
|
use_latest: bool,
|
||||||
.await?
|
config: &mut Config,
|
||||||
.trim()
|
) -> Result<(Height, Hash), error::Error> {
|
||||||
.parse()?;
|
if use_latest || config.trusted_height == 0 || config.trusted_hash.is_empty() {
|
||||||
|
let (trusted_height, trusted_hash) = latest_height_hash(&config.node_url)?;
|
||||||
|
config.trusted_hash = trusted_hash.to_string();
|
||||||
|
config.trusted_height = trusted_height.into();
|
||||||
|
|
||||||
let hash_path = base_path.join("trusted.hash");
|
Ok((trusted_height, trusted_hash))
|
||||||
let trusted_hash: Hash = fs::read_to_string(hash_path.as_path())
|
} else {
|
||||||
.await?
|
Ok((
|
||||||
.trim()
|
config.trusted_height.try_into()?,
|
||||||
.parse()?;
|
config
|
||||||
|
.trusted_hash
|
||||||
Ok((trusted_height, trusted_hash))
|
.parse()
|
||||||
|
.map_err(|_| error::Error::GenericErr("invalid hash".to_string()))?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries the chain for the latested height and hash
|
||||||
|
pub fn latest_height_hash(node_url: &String) -> Result<(Height, Hash), error::Error> {
|
||||||
|
let httpurl = Url::parse(&format!("http://{}", node_url))
|
||||||
|
.map_err(|e| error::Error::GenericErr(e.to_string()))?;
|
||||||
|
let wasmd_client = CliWasmdClient::new(httpurl);
|
||||||
|
|
||||||
|
let (trusted_height, trusted_hash) = wasmd_client
|
||||||
|
.trusted_height_hash()
|
||||||
|
.map_err(|e| error::Error::GenericErr(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
trusted_height.try_into()?,
|
||||||
|
trusted_hash.parse().expect("invalid hash from wasmd"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn persist_config_hash_height(config: &Config) -> Result<(), error::Error> {
|
||||||
|
let config_path = config.app_dir.join("quartz.toml");
|
||||||
|
|
||||||
|
let toml_content = fs::read_to_string(&config_path).await?;
|
||||||
|
let mut written_config: Config = toml::from_str(&toml_content)?;
|
||||||
|
|
||||||
|
written_config.trusted_hash.clone_from(&config.trusted_hash);
|
||||||
|
written_config.trusted_height = config.trusted_height;
|
||||||
|
|
||||||
|
let toml_string = toml::to_string(config)?;
|
||||||
|
fs::write(&config_path, toml_string).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,22 @@
|
||||||
)]
|
)]
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use cli::ToFigment;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use config::Config;
|
||||||
|
use figment::{
|
||||||
|
providers::{Env, Format, Serialized, Toml},
|
||||||
|
Figment,
|
||||||
|
};
|
||||||
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
|
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
|
||||||
|
|
||||||
use crate::{cli::Cli, handler::Handler, request::Request};
|
use crate::{cli::Cli, handler::Handler, request::Request};
|
||||||
|
@ -37,17 +46,26 @@ const BANNER: &str = r"
|
||||||
|
|
||||||
";
|
";
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
pub mock_sgx: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
println!("{BANNER}");
|
println!("{BANNER}");
|
||||||
|
|
||||||
let args = Cli::parse();
|
let args: Cli = Cli::parse();
|
||||||
|
check_path(&args.app_dir)?;
|
||||||
|
|
||||||
|
let config: Config = Figment::new()
|
||||||
|
.merge(Toml::file(
|
||||||
|
args.app_dir
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&PathBuf::from("."))
|
||||||
|
.join("quartz.toml"),
|
||||||
|
))
|
||||||
|
.merge(Env::prefixed("QUARTZ_"))
|
||||||
|
.merge(Serialized::defaults(&args))
|
||||||
|
.merge(args.command.to_figment())
|
||||||
|
.extract()?;
|
||||||
|
|
||||||
let env_filter = EnvFilter::builder()
|
let env_filter = EnvFilter::builder()
|
||||||
.with_default_directive(args.verbose.to_level_filter().into())
|
.with_default_directive(args.verbose.to_level_filter().into())
|
||||||
|
@ -67,11 +85,7 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
// Each `Request` defines an associated `Handler` (i.e. logic) and `Response`. All handlers are
|
// Each `Request` defines an associated `Handler` (i.e. logic) and `Response`. All handlers are
|
||||||
// free to log to the terminal and these logs are sent to `stderr`.
|
// free to log to the terminal and these logs are sent to `stderr`.
|
||||||
let response = request
|
let response = request.handle(config).await?;
|
||||||
.handle(Config {
|
|
||||||
mock_sgx: args.mock_sgx,
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// `Handlers` must use `Responses` to output to `stdout`.
|
// `Handlers` must use `Responses` to output to `stdout`.
|
||||||
println!(
|
println!(
|
||||||
|
@ -81,3 +95,13 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_path(path: &Option<PathBuf>) -> Result<(), error::Error> {
|
||||||
|
if let Some(path) = path {
|
||||||
|
if !path.is_dir() {
|
||||||
|
return Err(error::Error::PathNotDir(format!("{}", path.display())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::{env::current_dir, path::PathBuf};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::{Command, ContractCommand, EnclaveCommand},
|
cli::{Command, ContractCommand, EnclaveCommand},
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -32,23 +30,9 @@ impl TryFrom<Command> for Request {
|
||||||
|
|
||||||
fn try_from(cmd: Command) -> Result<Self, Self::Error> {
|
fn try_from(cmd: Command) -> Result<Self, Self::Error> {
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::Init { name } => Ok(InitRequest { name }.try_into()?),
|
Command::Init(args) => Ok(InitRequest { name: args.name }.try_into()?),
|
||||||
Command::Handshake {
|
Command::Handshake(args) => Ok(HandshakeRequest {
|
||||||
contract,
|
contract: args.contract,
|
||||||
port,
|
|
||||||
sender,
|
|
||||||
chain_id,
|
|
||||||
node_url,
|
|
||||||
enclave_rpc_addr,
|
|
||||||
app_dir,
|
|
||||||
} => Ok(HandshakeRequest {
|
|
||||||
contract,
|
|
||||||
port,
|
|
||||||
sender,
|
|
||||||
chain_id,
|
|
||||||
node_url,
|
|
||||||
enclave_rpc_addr,
|
|
||||||
app_dir: Self::path_checked(app_dir)?,
|
|
||||||
}
|
}
|
||||||
.into()),
|
.into()),
|
||||||
Command::Contract { contract_command } => contract_command.try_into(),
|
Command::Contract { contract_command } => contract_command.try_into(),
|
||||||
|
@ -57,53 +41,33 @@ impl TryFrom<Command> for Request {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
|
||||||
fn path_checked(path: Option<PathBuf>) -> Result<PathBuf, Error> {
|
|
||||||
if let Some(path) = path {
|
|
||||||
if !path.is_dir() {
|
|
||||||
return Err(Error::PathNotDir(format!("{}", path.display())));
|
|
||||||
}
|
|
||||||
Ok(path)
|
|
||||||
} else {
|
|
||||||
Ok(current_dir().map_err(|e| Error::GenericErr(e.to_string()))?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ContractCommand> for Request {
|
impl TryFrom<ContractCommand> for Request {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(cmd: ContractCommand) -> Result<Request, Error> {
|
fn try_from(cmd: ContractCommand) -> Result<Request, Error> {
|
||||||
match cmd {
|
match cmd {
|
||||||
ContractCommand::Deploy {
|
ContractCommand::Deploy(args) => {
|
||||||
init_msg,
|
if !args.wasm_bin_path.exists() {
|
||||||
node_url,
|
return Err(Error::PathNotFile(args.wasm_bin_path.display().to_string()));
|
||||||
chain_id,
|
|
||||||
sender,
|
|
||||||
label,
|
|
||||||
wasm_bin_path,
|
|
||||||
} => {
|
|
||||||
if !wasm_bin_path.exists() {
|
|
||||||
return Err(Error::PathNotFile(wasm_bin_path.display().to_string()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ContractDeployRequest {
|
Ok(ContractDeployRequest {
|
||||||
init_msg: serde_json::from_str(&init_msg)
|
init_msg: serde_json::from_str(&args.init_msg)
|
||||||
.map_err(|e| Error::GenericErr(e.to_string()))?,
|
.map_err(|e| Error::GenericErr(e.to_string()))?,
|
||||||
node_url,
|
label: args.label,
|
||||||
chain_id,
|
wasm_bin_path: args.wasm_bin_path,
|
||||||
sender,
|
|
||||||
label,
|
|
||||||
wasm_bin_path,
|
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
ContractCommand::Build { manifest_path } => {
|
ContractCommand::Build(args) => {
|
||||||
if !manifest_path.exists() {
|
if !args.manifest_path.exists() {
|
||||||
return Err(Error::PathNotFile(manifest_path.display().to_string()));
|
return Err(Error::PathNotFile(args.manifest_path.display().to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ContractBuildRequest { manifest_path }.into())
|
Ok(ContractBuildRequest {
|
||||||
|
manifest_path: args.manifest_path,
|
||||||
|
}
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,12 +78,12 @@ impl TryFrom<EnclaveCommand> for Request {
|
||||||
|
|
||||||
fn try_from(cmd: EnclaveCommand) -> Result<Request, Error> {
|
fn try_from(cmd: EnclaveCommand) -> Result<Request, Error> {
|
||||||
match cmd {
|
match cmd {
|
||||||
EnclaveCommand::Build { manifest_path } => {
|
EnclaveCommand::Build(args) => Ok(EnclaveBuildRequest {
|
||||||
Ok(EnclaveBuildRequest { manifest_path }.into())
|
manifest_path: args.manifest_path,
|
||||||
}
|
}
|
||||||
EnclaveCommand::Start { app_dir, chain_id } => Ok(EnclaveStartRequest {
|
.into()),
|
||||||
app_dir: Self::path_checked(app_dir)?,
|
EnclaveCommand::Start(args) => Ok(EnclaveStartRequest {
|
||||||
chain_id,
|
use_latest_trusted: args.use_latest_trusted,
|
||||||
}
|
}
|
||||||
.into()),
|
.into()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use cosmrs::tendermint::chain::Id as ChainId;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{error::Error, request::Request};
|
use crate::{error::Error, request::Request};
|
||||||
|
@ -8,9 +7,6 @@ use crate::{error::Error, request::Request};
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ContractDeployRequest {
|
pub struct ContractDeployRequest {
|
||||||
pub init_msg: serde_json::Value,
|
pub init_msg: serde_json::Value,
|
||||||
pub node_url: String,
|
|
||||||
pub chain_id: ChainId,
|
|
||||||
pub sender: String,
|
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub wasm_bin_path: PathBuf,
|
pub wasm_bin_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EnclaveStartRequest {
|
pub struct EnclaveStartRequest {
|
||||||
pub app_dir: PathBuf,
|
pub use_latest_trusted: bool,
|
||||||
pub chain_id: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EnclaveStartRequest> for Request {
|
impl From<EnclaveStartRequest> for Request {
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
use std::path::PathBuf;
|
use cosmrs::AccountId;
|
||||||
|
|
||||||
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
|
|
||||||
|
|
||||||
use crate::request::Request;
|
use crate::request::Request;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct HandshakeRequest {
|
pub struct HandshakeRequest {
|
||||||
pub contract: AccountId,
|
pub contract: AccountId,
|
||||||
pub port: u16,
|
|
||||||
pub sender: String,
|
|
||||||
pub chain_id: ChainId,
|
|
||||||
pub node_url: String,
|
|
||||||
pub enclave_rpc_addr: String,
|
|
||||||
pub app_dir: PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<HandshakeRequest> for Request {
|
impl From<HandshakeRequest> for Request {
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
use std::path::Path;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{error::Error, request::Request};
|
use crate::{error::Error, request::Request};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct InitRequest {
|
pub struct InitRequest {
|
||||||
pub name: String,
|
pub name: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<InitRequest> for Request {
|
impl TryFrom<InitRequest> for Request {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(request: InitRequest) -> Result<Request, Error> {
|
fn try_from(request: InitRequest) -> Result<Request, Error> {
|
||||||
if Path::new(&request.name).iter().count() != 1 {
|
if request.name.extension().is_some() {
|
||||||
return Err(Error::GenericErr("App name contains path".to_string()));
|
return Err(Error::PathNotDir(format!("{}", request.name.display())));
|
||||||
|
} else if request.name.exists() {
|
||||||
|
return Err(Error::GenericErr(format!(
|
||||||
|
"Directory already exists: {}",
|
||||||
|
request.name.display()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Request::Init(request))
|
Ok(Request::Init(request))
|
||||||
|
|
|
@ -51,6 +51,8 @@ pub trait WasmdClient {
|
||||||
init_msg: M,
|
init_msg: M,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> Result<String, Self::Error>;
|
) -> Result<String, Self::Error>;
|
||||||
|
|
||||||
|
fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
|
@ -227,4 +229,29 @@ impl WasmdClient for CliWasmdClient {
|
||||||
// TODO: find the rust type for the tx output and return that
|
// TODO: find the rust type for the tx output and return that
|
||||||
Ok((String::from_utf8(output.stdout)?).to_string())
|
Ok((String::from_utf8(output.stdout)?).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error> {
|
||||||
|
let mut wasmd = Command::new("wasmd");
|
||||||
|
let command = wasmd.args(["--node", self.url.as_str()]).arg("status");
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow!("{:?}", output));
|
||||||
|
}
|
||||||
|
|
||||||
|
let query_result: serde_json::Value =
|
||||||
|
serde_json::from_slice(&output.stdout).unwrap_or_default();
|
||||||
|
|
||||||
|
let trusted_height = query_result["SyncInfo"]["latest_block_height"]
|
||||||
|
.as_u64()
|
||||||
|
.ok_or(anyhow!("Could not query height"))?;
|
||||||
|
|
||||||
|
let trusted_hash = query_result["SyncInfo"]["latest_block_hash"]
|
||||||
|
.as_str()
|
||||||
|
.ok_or(anyhow!("Could not query height"))?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
Ok((trusted_height, trusted_hash))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue