Neutron client in pure rust (#234)

Implements the following:
- grpc based cw client (alsmost chain agnostic, but not fully yet)
- All URL based CLI args now use reqwest::Url type.
- An almost 'Bin agnostic CliClient'
- SSL and DNS resolution support in gramine
- Implement generic attestation support for transfers enclave
- Misc cleanup
This commit is contained in:
Shoaib Ahmed 2024-10-03 21:50:54 +04:00 committed by GitHub
parent 279cd5180f
commit 7133ea5e36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1141 additions and 672 deletions

243
Cargo.lock generated
View file

@ -90,6 +90,21 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.15"
@ -808,8 +823,11 @@ version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"serde",
"windows-targets 0.52.6",
]
[[package]]
@ -1016,20 +1034,21 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cosmos-sdk-proto"
version = "0.22.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d1a42d9ef55bb4e84b48ce29860f55859e52e0088ebdb1371cedd62f2b78cc"
checksum = "8b2f63ab112b8c8e7b8a29c891adc48f43145beb21c0bfbf562957072c1e0beb"
dependencies = [
"prost 0.12.6",
"prost-types 0.12.6",
"tendermint-proto 0.37.0",
"prost",
"prost-types",
"tendermint-proto",
"tonic",
]
[[package]]
name = "cosmrs"
version = "0.17.0"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85efbd5a3487e52ccd77897ef8f97b077a015953b46623fabc939f39d9c501fc"
checksum = "9f21bb63ec6a903510a3d01f44735dd4914724e5eccbe409e6da6833d17c7829"
dependencies = [
"cosmos-sdk-proto",
"ecdsa",
@ -1040,7 +1059,7 @@ dependencies = [
"serde_json",
"signature",
"subtle-encoding",
"tendermint 0.37.0",
"tendermint",
"thiserror",
]
@ -1265,11 +1284,17 @@ name = "cw-client"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"cosmos-sdk-proto",
"cosmrs",
"hex",
"reqwest 0.12.8",
"serde",
"serde_json",
"tendermint",
"tokio",
"tonic",
"transfers-contract",
]
[[package]]
@ -1285,7 +1310,7 @@ dependencies = [
"cw-utils",
"derivative",
"itertools 0.13.0",
"prost 0.13.3",
"prost",
"schemars",
"serde",
"sha2 0.10.8",
@ -1446,6 +1471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
"serde",
]
[[package]]
@ -2759,6 +2785,29 @@ dependencies = [
"tracing",
]
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core 0.52.0",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ics23"
version = "0.12.0"
@ -2770,7 +2819,7 @@ dependencies = [
"blake3",
"bytes",
"hex",
"prost 0.13.3",
"prost",
"ripemd",
"sha2 0.10.8",
"sha3",
@ -2862,6 +2911,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"serde",
]
[[package]]
@ -2965,15 +3015,6 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
@ -4110,16 +4151,6 @@ dependencies = [
"tokio-stream",
]
[[package]]
name = "prost"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
dependencies = [
"bytes",
"prost-derive 0.12.6",
]
[[package]]
name = "prost"
version = "0.13.3"
@ -4127,7 +4158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
dependencies = [
"bytes",
"prost-derive 0.13.3",
"prost-derive",
]
[[package]]
@ -4144,26 +4175,13 @@ dependencies = [
"once_cell",
"petgraph",
"prettyplease",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"regex",
"syn 2.0.79",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
dependencies = [
"anyhow",
"itertools 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.79",
]
[[package]]
name = "prost-derive"
version = "0.13.3"
@ -4177,22 +4195,13 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "prost-types"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
dependencies = [
"prost 0.12.6",
]
[[package]]
name = "prost-types"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
dependencies = [
"prost 0.13.3",
"prost",
]
[[package]]
@ -4233,10 +4242,10 @@ dependencies = [
"displaydoc",
"hex",
"ics23",
"prost 0.13.3",
"prost",
"serde",
"serde_with",
"tendermint 0.38.1",
"tendermint",
"tendermint-rpc",
]
@ -4249,7 +4258,7 @@ dependencies = [
"hex",
"quartz-cw-proof",
"serde_json",
"tendermint 0.38.1",
"tendermint",
"tendermint-rpc",
"tokio",
]
@ -4299,7 +4308,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.8",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-rpc",
"thiserror",
@ -4323,7 +4332,7 @@ dependencies = [
name = "quartz-proto"
version = "0.1.0"
dependencies = [
"prost 0.13.3",
"prost",
"tonic",
"tonic-build",
]
@ -4351,7 +4360,7 @@ dependencies = [
"k256",
"miette",
"once_cell",
"prost 0.13.3",
"prost",
"quartz-common",
"quartz-tee-ra",
"quartz-tm-prover",
@ -4359,10 +4368,11 @@ dependencies = [
"reqwest 0.12.8",
"serde",
"serde_json",
"serde_with",
"subtle-encoding",
"target-lexicon",
"tempfile",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-rpc",
"thiserror",
@ -4440,7 +4450,7 @@ dependencies = [
"quartz-cw-proof",
"serde",
"serde_json",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-light-client-detector",
"tendermint-rpc",
@ -4454,7 +4464,7 @@ name = "quartz-tm-stateless-verifier"
version = "0.1.0"
dependencies = [
"displaydoc",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
]
@ -5261,6 +5271,8 @@ dependencies = [
"base64 0.22.1",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.5.0",
"serde",
"serde_derive",
"serde_json",
@ -5567,37 +5579,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "tendermint"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "954496fbc9716eb4446cdd6d00c071a3e2f22578d62aa03b40c7e5b4fda3ed42"
dependencies = [
"bytes",
"digest 0.10.7",
"ed25519",
"ed25519-consensus",
"flex-error",
"futures",
"k256",
"num-traits",
"once_cell",
"prost 0.12.6",
"prost-types 0.12.6",
"ripemd",
"serde",
"serde_bytes",
"serde_json",
"serde_repr",
"sha2 0.10.8",
"signature",
"subtle",
"subtle-encoding",
"tendermint-proto 0.37.0",
"time",
"zeroize",
]
[[package]]
name = "tendermint"
version = "0.38.1"
@ -5610,10 +5591,12 @@ dependencies = [
"ed25519-consensus",
"flex-error",
"futures",
"k256",
"num-traits",
"once_cell",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"ripemd",
"serde",
"serde_bytes",
"serde_json",
@ -5622,7 +5605,7 @@ dependencies = [
"signature",
"subtle",
"subtle-encoding",
"tendermint-proto 0.38.1",
"tendermint-proto",
"time",
"zeroize",
]
@ -5636,7 +5619,7 @@ dependencies = [
"flex-error",
"serde",
"serde_json",
"tendermint 0.38.1",
"tendermint",
"toml",
"url",
]
@ -5658,7 +5641,7 @@ dependencies = [
"serde_derive",
"serde_json",
"static_assertions",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client-verifier",
"tendermint-rpc",
"time",
@ -5681,9 +5664,9 @@ dependencies = [
"serde_derive",
"serde_json",
"static_assertions",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-proto 0.38.1",
"tendermint-proto",
"tendermint-rpc",
"time",
"tracing",
@ -5698,23 +5681,7 @@ dependencies = [
"derive_more 0.99.18",
"flex-error",
"serde",
"tendermint 0.38.1",
"time",
]
[[package]]
name = "tendermint-proto"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc87024548c7f3da479885201e3da20ef29e85a3b13d04606b380ac4c7120d87"
dependencies = [
"bytes",
"flex-error",
"prost 0.12.6",
"prost-types 0.12.6",
"serde",
"serde_bytes",
"subtle-encoding",
"tendermint",
"time",
]
@ -5726,8 +5693,8 @@ checksum = "8ed14abe3b0502a3afe21ca74ca5cdd6c7e8d326d982c26f98a394445eb31d6e"
dependencies = [
"bytes",
"flex-error",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"serde",
"serde_bytes",
"subtle-encoding",
@ -5756,9 +5723,9 @@ dependencies = [
"serde_json",
"subtle",
"subtle-encoding",
"tendermint 0.38.1",
"tendermint",
"tendermint-config",
"tendermint-proto 0.38.1",
"tendermint-proto",
"thiserror",
"time",
"tokio",
@ -6033,7 +6000,7 @@ dependencies = [
"hyper-util",
"percent-encoding",
"pin-project",
"prost 0.13.3",
"prost",
"socket2",
"tokio",
"tokio-stream",
@ -6052,7 +6019,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
"prost-types 0.13.3",
"prost-types",
"quote",
"syn 2.0.79",
]
@ -6150,6 +6117,21 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "transfers-contract"
version = "0.1.0"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-storage-plus",
"cw-utils",
"getrandom",
"quartz-common",
"serde_json",
"sha2 0.10.8",
"thiserror",
]
[[package]]
name = "try-lock"
version = "0.2.5"
@ -6559,7 +6541,16 @@ version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core",
"windows-core 0.56.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.6",
]

View file

@ -96,8 +96,8 @@ x509-parser = { version = "0.16.0", default-features = false, features = [
zeroize = { version = "1.7.0", default-features = false }
# cosmos
cosmos-sdk-proto = { version = "0.22.0", default-features = false }
cosmrs = { version = "0.17.0", default-features = false }
cosmos-sdk-proto = { version = "0.23.0", default-features = false }
cosmrs = { version = "0.18.0", default-features = false }
cosmwasm-schema = { version = "2.1.1", default-features = false }
cosmwasm-std = { version = "2.1.1", default-features = false, features = [
"std",

View file

@ -47,6 +47,7 @@ toml = "0.8.19"
figment = { version = "0.10.19", features = ["env", "toml"] }
clearscreen = "3.0.0"
cargo_metadata = "0.18.1"
serde_with = "3.10.0"
# cosmos
cosmrs.workspace = true

View file

@ -4,7 +4,9 @@ use clap::{Parser, Subcommand};
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
use figment::{providers::Serialized, Figment};
use quartz_common::enclave::types::Fmspc;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use tracing::metadata::LevelFilter;
use crate::handler::utils::helpers::wasmaddr_to_id;
@ -79,6 +81,7 @@ pub enum Command {
Dev(DevArgs),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Subcommand, Serialize)]
pub enum ContractCommand {
Build(ContractBuildArgs),
@ -100,6 +103,7 @@ pub struct InitArgs {
pub name: PathBuf,
}
#[serde_as]
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
pub struct HandshakeArgs {
/// Path to create & init a Quartz app, defaults to current path if unspecified
@ -123,7 +127,20 @@ pub struct HandshakeArgs {
/// <host>:<port> to tendermint rpc interface for this chain
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_url: Option<String>,
#[serde_as(as = "Option<DisplayFromStr>")]
pub node_url: Option<Url>,
/// websocket URL
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<DisplayFromStr>")]
pub ws_url: Option<Url>,
/// gRPC URL
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<DisplayFromStr>")]
pub grpc_url: Option<Url>,
/// RPC interface for the Quartz enclave
#[arg(long)]
@ -143,6 +160,7 @@ pub struct ContractBuildArgs {
pub contract_manifest: PathBuf,
}
#[serde_as]
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
pub struct ContractDeployArgs {
/// Json-formatted cosmwasm contract initialization message
@ -152,7 +170,20 @@ pub struct ContractDeployArgs {
/// <host>:<port> to tendermint rpc interface for this chain
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_url: Option<String>,
#[serde_as(as = "Option<DisplayFromStr>")]
pub node_url: Option<Url>,
/// websocket URL
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<DisplayFromStr>")]
pub ws_url: Option<Url>,
/// gRPC URL
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<DisplayFromStr>")]
pub grpc_url: Option<Url>,
/// Name or address of private key with which to sign
#[arg(long)]
@ -192,6 +223,10 @@ pub struct EnclaveStartArgs {
#[arg(long)]
pub unsafe_trust_latest: bool,
/// file containing the secret key of the tx-sender for the enclave
#[arg(long)]
pub sk_file: PathBuf,
/// FMSPC (Family-Model-Stepping-Platform-Custom SKU); required if `MOCK_SGX` is not set
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -229,6 +264,10 @@ pub struct DevArgs {
#[command(flatten)]
pub enclave_build: EnclaveBuildArgs,
/// file containing the secret key of the tx-sender for the enclave
#[arg(long)]
pub sk_file: PathBuf,
/// FMSPC (Family-Model-Stepping-Platform-Custom SKU); required if `MOCK_SGX` is not set
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]

View file

@ -1,8 +1,11 @@
use std::path::PathBuf;
use cosmrs::tendermint::chain::Id as ChainId;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
/// Enable mock SGX mode for testing purposes.
@ -20,7 +23,18 @@ pub struct Config {
/// <host>:<port> to tendermint rpc interface for this chain
#[serde(default = "default_node_url")]
pub node_url: String,
#[serde_as(as = "DisplayFromStr")]
pub node_url: Url,
/// websocket URL
#[serde(default = "default_ws_url")]
#[serde_as(as = "DisplayFromStr")]
pub ws_url: Url,
/// gRPC URL
#[serde(default = "default_grpc_url")]
#[serde_as(as = "DisplayFromStr")]
pub grpc_url: Url,
/// RPC interface for the Quartz enclave
#[serde(default = "default_rpc_addr")]
@ -52,8 +66,22 @@ fn default_rpc_addr() -> String {
"http://127.0.0.1".to_string()
}
fn default_node_url() -> String {
"127.0.0.1:26657".to_string()
fn default_node_url() -> Url {
"http://127.0.0.1:26657"
.parse()
.expect("valid hardcoded URL")
}
fn default_ws_url() -> Url {
"ws://127.0.0.1/websocket"
.parse()
.expect("valid hardcoded URL")
}
fn default_grpc_url() -> Url {
"http://127.0.0.1:9090,"
.parse()
.expect("valid hardcoded URL")
}
fn default_tx_sender() -> String {
@ -79,6 +107,8 @@ impl Default for Config {
tx_sender: default_tx_sender(),
chain_id: default_chain_id(),
node_url: default_node_url(),
ws_url: default_ws_url(),
grpc_url: default_grpc_url(),
enclave_rpc_addr: default_rpc_addr(),
enclave_rpc_port: default_port(),
app_dir: default_app_dir(),

View file

@ -3,16 +3,12 @@ use std::path::Path;
use async_trait::async_trait;
use cargo_metadata::MetadataCommand;
use color_eyre::owo_colors::OwoColorize;
use cw_client::{CliWasmdClient, WasmdClient};
use reqwest::Url;
use cw_client::{CliClient, CwClient};
use serde_json::json;
use tendermint_rpc::HttpClient;
use tracing::{debug, info};
use super::utils::{
helpers::block_tx_commit,
types::{Log, WasmdTxResponse},
};
use super::utils::{helpers::block_tx_commit, types::WasmdTxResponse};
use crate::{
config::Config,
error::Error,
@ -68,9 +64,8 @@ async fn deploy(
args: ContractDeployRequest,
config: &Config,
) -> Result<(u64, String), anyhow::Error> {
let httpurl = Url::parse(&format!("http://{}", config.node_url))?;
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
let cw_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
let tmrpc_client = HttpClient::new(config.node_url.as_str())?;
let cw_client = CliClient::neutrond(config.node_url.clone());
info!("🚀 Deploying {} Contract", args.label);
let code_id = if config.contract_has_changed(wasm_bin_path).await? {
@ -81,8 +76,23 @@ async fn deploy(
)?)?;
let res = block_tx_commit(&tmrpc_client, deploy_output.txhash).await?;
let log: Vec<Log> = serde_json::from_str(&res.tx_result.log)?;
let code_id: u64 = log[0].events[1].attributes[1].value.parse()?;
// Find the 'code_id' attribute
let code_id = res
.tx_result
.events
.iter()
.find(|event| event.kind == "store_code")
.and_then(|event| {
event
.attributes
.iter()
.find(|attr| attr.key_str().unwrap_or("") == "code_id")
})
.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"))?;
info!("Code ID: {}", code_id);
config.save_codeid_to_cache(wasm_bin_path, code_id).await?;
code_id
@ -108,8 +118,22 @@ async fn deploy(
)?)?;
let res = block_tx_commit(&tmrpc_client, init_output.txhash).await?;
let log: Vec<Log> = serde_json::from_str(&res.tx_result.log)?;
let contract_addr: &String = &log[0].events[1].attributes[0].value;
// Find the '_contract_address' attribute
let contract_addr: String = res
.tx_result
.events
.iter()
.find(|event| event.kind == "instantiate")
.and_then(|event| {
event
.attributes
.iter()
.find(|attr| attr.key_str().unwrap_or("") == "_contract_address")
})
.and_then(|attr| attr.value_str().ok().and_then(|v| v.parse().ok()))
.ok_or_else(|| {
anyhow::anyhow!("Failed to find contract_address in the transaction result")
})?;
info!("🚀 Successfully deployed and instantiated contract!");
info!("🆔 Code ID: {}", code_id);

View file

@ -170,6 +170,7 @@ async fn dev_driver(
fn spawn_enclave_start(args: &DevRequest, config: &Config) -> Result<(), Error> {
// In separate process, launch the enclave
let enclave_start = EnclaveStartRequest {
sk_file: args.sk_file.clone(),
unsafe_trust_latest: args.unsafe_trust_latest,
fmspc: args.fmspc.clone(),
tcbinfo_contract: args.tcbinfo_contract.clone(),

View file

@ -5,6 +5,8 @@ use cargo_metadata::MetadataCommand;
use color_eyre::owo_colors::OwoColorize;
use cosmrs::AccountId;
use quartz_common::enclave::types::Fmspc;
use reqwest::Url;
use tendermint::chain::Id;
use tokio::process::{Child, Command};
use tracing::{debug, info};
@ -41,7 +43,11 @@ impl Handler for EnclaveStartRequest {
"--trusted-hash".to_string(),
trusted_hash.to_string(),
"--node-url".to_string(),
config.node_url,
config.node_url.to_string(),
"--ws-url".to_string(),
config.ws_url.to_string(),
"--grpc-url".to_string(),
config.grpc_url.to_string(),
"--tx-sender".to_string(),
config.tx_sender,
];
@ -77,15 +83,22 @@ impl Handler for EnclaveStartRequest {
// gramine manifest
let quartz_dir_canon = &enclave_dir.join("..");
debug!("quartz_dir_canon: {:?}", quartz_dir_canon);
gramine_manifest(
&trusted_height.to_string(),
&trusted_hash.to_string(),
&config.chain_id,
quartz_dir_canon,
&enclave_dir,
&self.sk_file,
fmspc,
tcbinfo_contract,
dcap_verifier_contract,
&config.node_url,
&config.ws_url,
&config.grpc_url,
)
.await?;
@ -174,15 +187,20 @@ async fn gramine_sgx_gen_private_key(enclave_dir: &Path) -> Result<(), Error> {
Ok(())
}
#[allow(clippy::too_many_arguments)]
async fn gramine_manifest(
trusted_height: &str,
trusted_hash: &str,
chain_id: &Id,
quartz_dir: &Path,
enclave_dir: &Path,
sk_file: &Path,
fmspc: Fmspc,
tcbinfo_contract: AccountId,
dcap_verifier_contract: AccountId,
node_url: &str,
node_url: &Url,
ws_url: &Url,
grpc_url: &Url,
) -> Result<(), Error> {
let host = target_lexicon::HOST;
let arch_libdir = format!(
@ -203,11 +221,15 @@ async fn gramine_manifest(
.arg("-Dra_type=dcap")
.arg(format!("-Dra_client_spid={}", ra_client_spid))
.arg("-Dra_client_linkable=1")
.arg(format!("-Dchain_id={}", chain_id))
.arg(format!("-Dquartz_dir={}", quartz_dir.display()))
.arg(format!("-Dtrusted_height={}", trusted_height))
.arg(format!("-Dtrusted_hash={}", trusted_hash))
.arg(format!("-Dsk_file={}", sk_file.display()))
.arg(format!("-Dfmspc={}", hex::encode(fmspc)))
.arg(format!("-Dnode_url={}", node_url))
.arg(format!("-Dws_url={}", ws_url))
.arg(format!("-Dgrpc_url={}", grpc_url))
.arg(format!("-Dtcbinfo_contract={}", tcbinfo_contract))
.arg(format!(
"-Ddcap_verifier_contract={}",

View file

@ -1,13 +1,9 @@
use std::str::FromStr;
use anyhow::anyhow;
use async_trait::async_trait;
use color_eyre::owo_colors::OwoColorize;
use cosmrs::tendermint::chain::Id as ChainId; // TODO see if this redundancy in dependencies can be decreased
use cw_client::{CliWasmdClient, WasmdClient};
use cw_client::{CliClient, CwClient};
use futures_util::stream::StreamExt;
use quartz_tm_prover::{config::Config as TmProverConfig, prover::prove};
use reqwest::Url;
use serde_json::json;
use tendermint_rpc::{query::EventType, HttpClient, SubscriptionClient, WebSocketClient};
use tracing::{debug, info};
@ -47,11 +43,8 @@ impl Handler for HandshakeRequest {
}
async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, anyhow::Error> {
let httpurl = Url::parse(&format!("http://{}", config.node_url))?;
let wsurl = format!("ws://{}/websocket", config.node_url);
let tmrpc_client = HttpClient::new(httpurl.as_str())?;
let cw_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);
let tmrpc_client = HttpClient::new(config.node_url.as_str())?;
let cw_client = CliClient::neutrond(config.node_url.clone());
let (trusted_height, trusted_hash) = read_cached_hash_height(&config).await?;
@ -69,7 +62,9 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
2000000,
&config.tx_sender,
json!(res),
)?
"11000untrn",
)
.await?
.as_str(),
)?;
debug!("\n\n SessionCreate tx output: {:?}", output);
@ -80,12 +75,12 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
// Wait 2 blocks
info!("Waiting 2 blocks for light client proof");
two_block_waitoor(&wsurl).await?;
two_block_waitoor(config.ws_url.as_str()).await?;
// Call tm prover with trusted hash and height
let prover_config = TmProverConfig {
primary: httpurl.as_str().parse()?,
witnesses: httpurl.as_str().parse()?,
primary: config.node_url.as_str().parse()?,
witnesses: config.node_url.as_str().parse()?,
trusted_height,
trusted_hash,
verbose: "1".parse()?, // TODO: both tm-prover and cli define the same Verbosity struct. Need to define this once and import
@ -112,11 +107,13 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
cw_client
.tx_execute(
&args.contract.clone(),
&ChainId::from_str("testing")?,
&config.chain_id,
2000000,
&config.tx_sender,
json!(res),
)?
"11000untrn",
)
.await?
.as_str(),
)?;

View file

@ -2,7 +2,7 @@ use std::time::Duration;
use anyhow::anyhow;
use cosmrs::{AccountId, ErrorReport};
use cw_client::{CliWasmdClient, WasmdClient};
use cw_client::{CliClient, CwClient};
use regex::Regex;
use reqwest::Url;
use subtle_encoding::bech32::decode as bech32_decode;
@ -16,11 +16,7 @@ use tracing::debug;
use crate::{config::Config, error::Error};
pub fn wasmaddr_to_id(address_str: &str) -> Result<AccountId, anyhow::Error> {
let (hr, _) = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
if hr != "wasm" {
return Err(anyhow!(hr));
}
let _ = bech32_decode(address_str).map_err(|e| anyhow!(e))?;
address_str.parse().map_err(|e: ErrorReport| anyhow!(e))
}
@ -59,10 +55,8 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
}
// Queries the chain for the latested height and hash
pub fn query_latest_height_hash(node_url: &String) -> Result<(Height, Hash), Error> {
let httpurl = Url::parse(&format!("http://{}", node_url))
.map_err(|e| Error::GenericErr(e.to_string()))?;
let cw_client = CliWasmdClient::new(httpurl);
pub fn query_latest_height_hash(node_url: Url) -> Result<(Height, Hash), Error> {
let cw_client = CliClient::neutrond(node_url);
let (trusted_height, trusted_hash) = cw_client
.trusted_height_hash()

View file

@ -48,6 +48,7 @@ impl TryFrom<Command> for Request {
label: args.contract_deploy.label,
contract_manifest: args.contract_deploy.contract_manifest,
release: args.enclave_build.release,
sk_file: args.sk_file,
fmspc: args.fmspc,
tcbinfo_contract: args.tcbinfo_contract,
dcap_verifier_contract: args.dcap_verifier_contract,
@ -101,6 +102,7 @@ impl TryFrom<EnclaveCommand> for Request {
EnclaveCommand::Build(_) => Ok(EnclaveBuildRequest {}.into()),
EnclaveCommand::Start(args) => Ok(EnclaveStartRequest {
unsafe_trust_latest: args.unsafe_trust_latest,
sk_file: args.sk_file,
fmspc: args.fmspc,
tcbinfo_contract: args.tcbinfo_contract,
dcap_verifier_contract: args.dcap_verifier_contract,

View file

@ -13,6 +13,7 @@ pub struct DevRequest {
pub label: String,
pub contract_manifest: PathBuf,
pub release: bool,
pub sk_file: PathBuf,
pub fmspc: Option<Fmspc>,
pub tcbinfo_contract: Option<AccountId>,
pub dcap_verifier_contract: Option<AccountId>,

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use cosmrs::AccountId;
use quartz_common::enclave::types::Fmspc;
use tendermint::{block::Height, Hash};
@ -11,6 +13,7 @@ use crate::{
#[derive(Clone, Debug)]
pub struct EnclaveStartRequest {
pub unsafe_trust_latest: bool,
pub sk_file: PathBuf,
pub fmspc: Option<Fmspc>,
pub tcbinfo_contract: Option<AccountId>,
pub dcap_verifier_contract: Option<AccountId>,
@ -28,7 +31,7 @@ impl EnclaveStartRequest {
if self.unsafe_trust_latest || config.trusted_height == 0 || config.trusted_hash.is_empty()
{
debug!("querying latest trusted hash & height from node");
let (trusted_height, trusted_hash) = query_latest_height_hash(&config.node_url)?;
let (trusted_height, trusted_hash) = query_latest_height_hash(config.node_url.clone())?;
Ok((trusted_height, trusted_hash))
} else {

View file

@ -1,6 +1,7 @@
use std::{
convert::Infallible,
net::SocketAddr,
path::PathBuf,
sync::{Arc, Mutex},
time::Duration,
};
@ -31,6 +32,7 @@ use quartz_proto::quartz::{
};
use quartz_tm_stateless_verifier::make_provider;
use rand::Rng;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use tendermint::{block::Height, Hash};
use tendermint_light_client::{
@ -78,11 +80,14 @@ pub trait WebSocketHandler: Send + Sync + 'static {
#[derive(Debug, Clone)]
pub struct WsListenerConfig {
pub node_url: String,
pub node_url: Url,
pub ws_url: Url,
pub grpc_url: Url,
pub chain_id: String,
pub tx_sender: String,
pub trusted_hash: Hash,
pub trusted_height: Height,
pub sk_file: PathBuf,
}
/// A trait for wrapping a tonic service with the gRPC server handler
@ -154,8 +159,9 @@ impl QuartzServer {
ws_handlers: &Vec<Box<dyn WebSocketHandler>>,
ws_config: WsListenerConfig,
) -> Result<(), QuartzError> {
let wsurl = format!("ws://{}/websocket", ws_config.node_url);
let (client, driver) = WebSocketClient::new(wsurl.as_str()).await.unwrap();
let (client, driver) = WebSocketClient::new(ws_config.ws_url.as_str())
.await
.unwrap();
let driver_handle = tokio::spawn(async move { driver.run().await });
let mut subs = client.subscribe(Query::from(EventType::Tx)).await.unwrap();

View file

@ -1,6 +1,6 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.out_dir("src/prost")
.compile(&["proto/quartz.proto"], &["proto"])?;
.compile_protos(&["proto/quartz.proto"], &["proto"])?;
Ok(())
}

View file

@ -9,12 +9,21 @@ repository.workspace = true
authors.workspace = true
[lib]
path="src/lib.rs"
path = "src/lib.rs"
[dependencies]
cosmrs.workspace = true
async-trait.workspace = true
hex.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
anyhow.workspace = true
anyhow.workspace = true
tonic.workspace = true
cosmrs = { workspace = true, default-features = false, features = ["cosmwasm"] }
cosmos-sdk-proto = { workspace = true, default-features = false, features = ["grpc", "grpc-transport"] }
tendermint = { workspace = true, default-features = false }
[dev-dependencies]
tokio.workspace = true
transfers-contract = { path = "../../../examples/transfers/contracts" }

View file

@ -0,0 +1 @@
ffc4d3c9119e9e8263de08c0f6e2368ac5c2dacecfeb393f6813da7d178873d2

View file

@ -0,0 +1,255 @@
use std::process::Command;
use anyhow::anyhow;
use cosmrs::{tendermint::chain::Id, AccountId};
use reqwest::Url;
use serde::de::DeserializeOwned;
use crate::CwClient;
#[derive(Clone, Debug)]
pub enum CliClientType {
Wasmd,
Neutrond,
}
impl CliClientType {
fn bin(&self) -> String {
match self {
CliClientType::Wasmd => "wasmd",
CliClientType::Neutrond => "neutrond",
}
.to_string()
}
}
#[derive(Clone, Debug)]
pub struct CliClient {
kind: CliClientType,
url: Url,
gas_price: String,
}
impl CliClient {
pub fn new(kind: CliClientType, url: Url, gas_price: String) -> Self {
Self {
kind,
url,
gas_price,
}
}
pub fn wasmd(url: Url) -> Self {
Self {
kind: CliClientType::Wasmd,
url,
gas_price: "0.0025ucosm".to_string(),
}
}
pub fn neutrond(url: Url) -> Self {
Self {
kind: CliClientType::Neutrond,
url,
gas_price: "0.0053untrn".to_string(),
}
}
fn new_command(&self) -> Command {
Command::new(self.kind.bin())
}
}
#[async_trait::async_trait]
impl CwClient for CliClient {
type Address = AccountId;
type Query = serde_json::Value;
type RawQuery = String;
type ChainId = Id;
type Error = anyhow::Error;
async fn query_smart<R: DeserializeOwned + Send>(
&self,
contract: &Self::Address,
query: Self::Query,
) -> Result<R, Self::Error> {
let mut command = self.new_command();
let command = command
.args(["--node", self.url.as_str()])
.args(["query", "wasm"])
.args(["contract-state", "smart", contract.as_ref()])
.arg(query.to_string())
.args(["--output", "json"]);
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
let query_result: R = serde_json::from_slice(&output.stdout)
.map_err(|e| anyhow!("Error deserializing: {}", e))?;
Ok(query_result)
}
fn query_raw<R: DeserializeOwned + Default>(
&self,
contract: &Self::Address,
query: Self::RawQuery,
) -> Result<R, Self::Error> {
let mut command = self.new_command();
let command = command
.args(["--node", self.url.as_str()])
.args(["query", "wasm"])
.args(["contract-state", "raw", contract.as_ref()])
.arg(&query)
.args(["--output", "json"]);
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
Ok(query_result)
}
fn query_tx<R: DeserializeOwned + Default>(&self, txhash: &str) -> Result<R, Self::Error> {
let mut command = self.new_command();
let command = command
.args(["--node", self.url.as_str()])
.args(["query", "tx"])
.arg(txhash)
.args(["--output", "json"]);
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
Ok(query_result)
}
async fn tx_execute<M: ToString + Send>(
&self,
contract: &Self::Address,
chain_id: &Id,
gas: u64,
sender: &str,
msg: M,
fees: &str,
) -> Result<String, Self::Error> {
let mut command = self.new_command();
let command = command
.args(["--node", self.url.as_str()])
.args(["--chain-id", chain_id.as_ref()])
.args(["tx", "wasm"])
.args(["execute", contract.as_ref(), &msg.to_string()])
.args(["--gas", &gas.to_string()])
.args(["--fees", fees])
.args(["--from", sender])
.args(["--output", "json"])
.arg("-y");
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
// TODO: find the rust type for the tx output and return that
Ok((String::from_utf8(output.stdout)?).to_string())
}
fn deploy<M: ToString>(
&self,
chain_id: &Id,
sender: &str,
wasm_path: M,
) -> Result<String, Self::Error> {
let mut command = self.new_command();
let command = command
.args(["--node", self.url.as_str()])
.args(["tx", "wasm", "store", &wasm_path.to_string()])
.args(["--from", sender])
.args(["--chain-id", chain_id.as_ref()])
.args(["--gas-prices", &self.gas_price])
.args(["--gas", "auto"])
.args(["--gas-adjustment", "1.3"])
.args(["-o", "json"])
.arg("-y");
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
// TODO: find the rust type for the tx output and return that
Ok((String::from_utf8(output.stdout)?).to_string())
}
fn init<M: ToString>(
&self,
chain_id: &Id,
sender: &str,
code_id: u64,
init_msg: M,
label: &str,
) -> Result<String, Self::Error> {
let mut command = self.new_command();
let command = command
.args(["--node", self.url.as_str()])
.args(["tx", "wasm", "instantiate"])
.args([&code_id.to_string(), &init_msg.to_string()])
.args(["--label", label])
.args(["--from", sender])
.arg("--no-admin")
.args(["--chain-id", chain_id.as_ref()])
.args(["--gas-prices", &self.gas_price])
.args(["--gas", "auto"])
.args(["--gas-adjustment", "1.3"])
.args(["-o", "json"])
.arg("-y");
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
// TODO: find the rust type for the tx output and return that
Ok((String::from_utf8(output.stdout)?).to_string())
}
fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error> {
let mut command = self.new_command();
let command = command.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 sync_info = match self.kind {
CliClientType::Wasmd => "SyncInfo",
CliClientType::Neutrond => "sync_info",
};
let trusted_height = query_result[sync_info]["latest_block_height"]
.as_str()
.ok_or(anyhow!("Could not query height"))?;
let trusted_height = trusted_height.parse::<u64>()?;
let trusted_hash = query_result[sync_info]["latest_block_hash"]
.as_str()
.ok_or(anyhow!("Could not query height"))?
.to_string();
Ok((trusted_height, trusted_hash))
}
}

View file

@ -0,0 +1,269 @@
use std::{error::Error, fs::File, io::Read, path::PathBuf};
use anyhow::anyhow;
use cosmos_sdk_proto::{
cosmos::{
auth::v1beta1::{
query_client::QueryClient as AuthQueryClient, BaseAccount as RawBaseAccount,
QueryAccountRequest,
},
tx::v1beta1::{
service_client::ServiceClient, BroadcastMode, BroadcastTxRequest, BroadcastTxResponse,
},
},
cosmwasm::wasm::v1::{
query_client::QueryClient as WasmdQueryClient, QuerySmartContractStateRequest,
},
traits::Message,
Any,
};
use cosmrs::{
auth::BaseAccount,
cosmwasm::MsgExecuteContract,
crypto::{secp256k1::SigningKey, PublicKey},
tendermint::chain::Id as TmChainId,
tx,
tx::{Fee, Msg, SignDoc, SignerInfo},
AccountId, Coin,
};
use reqwest::Url;
use serde::de::DeserializeOwned;
use crate::CwClient;
#[derive(Clone, Debug)]
pub struct GrpcClient {
sk_file: PathBuf,
url: Url,
}
impl GrpcClient {
pub fn new(sk_file: PathBuf, url: Url) -> Self {
Self { sk_file, url }
}
}
#[async_trait::async_trait]
impl CwClient for GrpcClient {
type Address = AccountId;
type Query = serde_json::Value;
type RawQuery = String;
type ChainId = TmChainId;
type Error = anyhow::Error;
async fn query_smart<R: DeserializeOwned + Send>(
&self,
contract: &Self::Address,
query: Self::Query,
) -> Result<R, Self::Error> {
let mut client = WasmdQueryClient::connect(self.url.to_string()).await?;
let raw_query_request = QuerySmartContractStateRequest {
address: contract.to_string(),
query_data: query.to_string().into_bytes(),
};
let raw_query_response = client.smart_contract_state(raw_query_request).await?;
let raw_value = raw_query_response.into_inner().data;
serde_json::from_slice(&raw_value)
.map_err(|e| anyhow!("failed to deserialize JSON reponse: {}", e))
}
fn query_raw<R: DeserializeOwned + Default>(
&self,
_contract: &Self::Address,
_query: Self::RawQuery,
) -> Result<R, Self::Error> {
unimplemented!()
}
fn query_tx<R: DeserializeOwned + Default>(&self, _txhash: &str) -> Result<R, Self::Error> {
unimplemented!()
}
async fn tx_execute<M: ToString + Send>(
&self,
contract: &Self::Address,
chain_id: &TmChainId,
gas: u64,
_sender: &str,
msg: M,
_fees: &str,
) -> Result<String, Self::Error> {
let secret = {
let mut secret_hex = String::new();
let mut sk_file = File::open(self.sk_file.clone())?;
sk_file.read_to_string(&mut secret_hex)?;
let secret = hex::decode(secret_hex)?;
SigningKey::from_slice(&secret)
.map_err(|e| anyhow!("failed to read secret key: {}", e))?
};
let tm_pubkey = secret.public_key();
let sender = tm_pubkey
.account_id("neutron")
.map_err(|e| anyhow!("failed to create AccountId from pubkey: {}", e))?;
let msgs = vec![MsgExecuteContract {
sender: sender.clone(),
contract: contract.clone(),
msg: msg.to_string().into_bytes(),
funds: vec![],
}
.to_any()
.unwrap()];
let account = account_info(self.url.to_string(), sender.to_string())
.await
.map_err(|e| anyhow!("error querying account info: {}", e))?;
let amount = Coin {
amount: 11000u128,
denom: "untrn".parse().expect("hardcoded denom"),
};
let tx_bytes = tx_bytes(
&secret,
amount,
gas,
tm_pubkey,
msgs,
account.sequence,
account.account_number,
chain_id,
)
.map_err(|e| anyhow!("failed to create msg/tx: {}", e))?;
let response = send_tx(self.url.to_string(), tx_bytes)
.await
.map_err(|e| anyhow!("failed to send tx: {}", e))?;
println!("{response:?}");
Ok(response
.tx_response
.map(|tx_response| tx_response.txhash)
.unwrap_or_default())
}
fn deploy<M: ToString>(
&self,
_chain_id: &TmChainId,
_sender: &str,
_wasm_path: M,
) -> Result<String, Self::Error> {
unimplemented!()
}
fn init<M: ToString>(
&self,
_chain_id: &TmChainId,
_sender: &str,
_code_id: u64,
_init_msg: M,
_label: &str,
) -> Result<String, Self::Error> {
unimplemented!()
}
fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error> {
unimplemented!()
}
}
pub async fn account_info(
node: impl ToString,
address: impl ToString,
) -> Result<BaseAccount, Box<dyn Error>> {
let mut client = AuthQueryClient::connect(node.to_string()).await?;
let request = tonic::Request::new(QueryAccountRequest {
address: address.to_string(),
});
let response = client.account(request).await?;
let response = RawBaseAccount::decode(response.into_inner().account.unwrap().value.as_slice())?;
let account = BaseAccount::try_from(response)?;
Ok(account)
}
#[allow(clippy::too_many_arguments)]
pub fn tx_bytes(
secret: &SigningKey,
amount: Coin,
gas: u64,
tm_pubkey: PublicKey,
msgs: Vec<Any>,
sequence_number: u64,
account_number: u64,
chain_id: &TmChainId,
) -> Result<Vec<u8>, Box<dyn Error>> {
let tx_body = tx::Body::new(msgs, "", 0u16);
let signer_info = SignerInfo::single_direct(Some(tm_pubkey), sequence_number);
let auth_info = signer_info.auth_info(Fee::from_amount_and_gas(amount, gas));
let sign_doc = SignDoc::new(&tx_body, &auth_info, chain_id, account_number)?;
let tx_signed = sign_doc.sign(secret)?;
Ok(tx_signed.to_bytes()?)
}
pub async fn send_tx(
node: impl ToString,
tx_bytes: Vec<u8>,
) -> Result<BroadcastTxResponse, Box<dyn Error>> {
let mut client = ServiceClient::connect(node.to_string()).await?;
let request = tonic::Request::new(BroadcastTxRequest {
tx_bytes,
mode: BroadcastMode::Sync.into(),
});
let tx_response = client.broadcast_tx(request).await?;
Ok(tx_response.into_inner())
}
#[cfg(test)]
mod tests {
use std::error::Error;
use serde_json::json;
use transfers_contract::msg::{execute::Request, QueryMsg::GetRequests};
use crate::{CwClient, GrpcClient};
#[tokio::test]
#[ignore]
async fn test_query() -> Result<(), Box<dyn Error>> {
let sk_file = "../data/admin.sk".parse().unwrap();
let url = "https://grpc-falcron.pion-1.ntrn.tech:80".parse().unwrap();
let contract = "neutron15ruzx9wvrupt9cffzsp6868uad2svhfym2nsgxm2skpeqr3qrd4q4uwk83"
.parse()
.unwrap();
let cw_client = GrpcClient::new(sk_file, url);
let resp: Vec<Request> = cw_client
.query_smart(&contract, json!(GetRequests {}))
.await?;
println!("{resp:?}");
Ok(())
}
#[tokio::test]
#[ignore]
async fn test_execute() -> Result<(), Box<dyn Error>> {
let sk_file = "data/admin.sk".parse().unwrap();
let url = "https://grpc-falcron.pion-1.ntrn.tech:80".parse().unwrap();
let contract = "neutron15ruzx9wvrupt9cffzsp6868uad2svhfym2nsgxm2skpeqr3qrd4q4uwk83"
.parse()
.unwrap();
let chain_id = "pion-1".parse().unwrap();
let cw_client = GrpcClient::new(sk_file, url);
let tx_hash = cw_client
.tx_execute(
&contract,
&chain_id,
2000000,
"/* unused since we're getting the account from the sk */",
json!([]),
"11000untrn",
)
.await?;
println!("{}", tx_hash);
Ok(())
}
}

View file

@ -1,19 +1,22 @@
use std::process::Command;
use anyhow::anyhow;
use cosmrs::{tendermint::chain::Id, AccountId};
pub use cli::CliClient;
use cosmrs::tendermint::chain::Id;
pub use grpc::GrpcClient;
use hex::ToHex;
use reqwest::Url;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde::de::DeserializeOwned;
pub trait WasmdClient {
pub mod cli;
pub mod grpc;
#[async_trait::async_trait]
pub trait CwClient {
type Address: AsRef<str>;
type Query: ToString;
type RawQuery: ToHex;
type ChainId: AsRef<str>;
type Error;
fn query_smart<R: DeserializeOwned>(
async fn query_smart<R: DeserializeOwned + Send>(
&self,
contract: &Self::Address,
query: Self::Query,
@ -27,13 +30,14 @@ pub trait WasmdClient {
fn query_tx<R: DeserializeOwned + Default>(&self, txhash: &str) -> Result<R, Self::Error>;
fn tx_execute<M: ToString>(
async fn tx_execute<M: ToString + Send>(
&self,
contract: &Self::Address,
chain_id: &Id,
gas: u64,
sender: &str,
msg: M,
fees: &str,
) -> Result<String, Self::Error>;
fn deploy<M: ToString>(
@ -54,206 +58,3 @@ pub trait WasmdClient {
fn trusted_height_hash(&self) -> Result<(u64, String), Self::Error>;
}
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct QueryResult<T> {
pub data: T,
}
#[derive(Clone, Debug)]
pub struct CliWasmdClient {
url: Url,
}
impl CliWasmdClient {
pub fn new(url: Url) -> Self {
Self { url }
}
}
impl WasmdClient for CliWasmdClient {
type Address = AccountId;
type Query = serde_json::Value;
type RawQuery = String;
type ChainId = Id;
type Error = anyhow::Error;
fn query_smart<R: DeserializeOwned>(
&self,
contract: &Self::Address,
query: Self::Query,
) -> Result<R, Self::Error> {
let mut wasmd = Command::new("wasmd");
let command = wasmd
.args(["--node", self.url.as_str()])
.args(["query", "wasm"])
.args(["contract-state", "smart", contract.as_ref()])
.arg(query.to_string())
.args(["--output", "json"]);
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
let query_result: R = serde_json::from_slice(&output.stdout)
.map_err(|e| anyhow!("Error deserializing: {}", e))?;
Ok(query_result)
}
fn query_raw<R: DeserializeOwned + Default>(
&self,
contract: &Self::Address,
query: Self::RawQuery,
) -> Result<R, Self::Error> {
let mut wasmd = Command::new("wasmd");
let command = wasmd
.args(["--node", self.url.as_str()])
.args(["query", "wasm"])
.args(["contract-state", "raw", contract.as_ref()])
.arg(&query)
.args(["--output", "json"]);
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
Ok(query_result)
}
fn query_tx<R: DeserializeOwned + Default>(&self, txhash: &str) -> Result<R, Self::Error> {
let mut wasmd = Command::new("wasmd");
let command = wasmd
.args(["--node", self.url.as_str()])
.args(["query", "tx"])
.arg(txhash)
.args(["--output", "json"]);
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
let query_result: R = serde_json::from_slice(&output.stdout).unwrap_or_default();
Ok(query_result)
}
fn tx_execute<M: ToString>(
&self,
contract: &Self::Address,
chain_id: &Id,
gas: u64,
sender: &str,
msg: M,
) -> Result<String, Self::Error> {
let mut wasmd = Command::new("wasmd");
let command = wasmd
.args(["--node", self.url.as_str()])
.args(["--chain-id", chain_id.as_ref()])
.args(["tx", "wasm"])
.args(["execute", contract.as_ref(), &msg.to_string()])
.args(["--gas", &gas.to_string()])
.args(["--from", sender])
.args(["--output", "json"])
.arg("-y");
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
// TODO: find the rust type for the tx output and return that
Ok((String::from_utf8(output.stdout)?).to_string())
}
fn deploy<M: ToString>(
&self,
chain_id: &Id,
sender: &str,
wasm_path: M,
) -> Result<String, Self::Error> {
let mut wasmd = Command::new("wasmd");
let command = wasmd
.args(["--node", self.url.as_str()])
.args(["tx", "wasm", "store", &wasm_path.to_string()])
.args(["--from", sender])
.args(["--chain-id", chain_id.as_ref()])
.args(["--gas-prices", "0.0025ucosm"])
.args(["--gas", "auto"])
.args(["--gas-adjustment", "1.3"])
.args(["-o", "json"])
.arg("-y");
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
// TODO: find the rust type for the tx output and return that
Ok((String::from_utf8(output.stdout)?).to_string())
}
fn init<M: ToString>(
&self,
chain_id: &Id,
sender: &str,
code_id: u64,
init_msg: M,
label: &str,
) -> Result<String, Self::Error> {
let mut wasmd = Command::new("wasmd");
let command = wasmd
.args(["--node", self.url.as_str()])
.args(["tx", "wasm", "instantiate"])
.args([&code_id.to_string(), &init_msg.to_string()])
.args(["--label", label])
.args(["--from", sender])
.arg("--no-admin")
.args(["--chain-id", chain_id.as_ref()])
.args(["--gas-prices", "0.0025ucosm"])
.args(["--gas", "auto"])
.args(["--gas-adjustment", "1.3"])
.args(["-o", "json"])
.arg("-y");
let output = command.output()?;
if !output.status.success() {
return Err(anyhow!("{:?}", output));
}
// TODO: find the rust type for the tx output and return that
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_str()
.ok_or(anyhow!("Could not query height"))?;
let trusted_height = trusted_height.parse::<u64>()?;
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))
}
}

View file

@ -24,7 +24,7 @@ pub enum QueryMsg {
pub enum ExecuteMsg<RA = RawDefaultAttestation> {
// quartz initialization
Quartz(QuartzExecuteMsg<RA>),
// User msgs
// clear text
Deposit,

View file

@ -268,9 +268,9 @@ dependencies = [
[[package]]
name = "async-stream"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
@ -279,9 +279,9 @@ dependencies = [
[[package]]
name = "async-stream-impl"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
@ -708,20 +708,21 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cosmos-sdk-proto"
version = "0.22.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d1a42d9ef55bb4e84b48ce29860f55859e52e0088ebdb1371cedd62f2b78cc"
checksum = "8b2f63ab112b8c8e7b8a29c891adc48f43145beb21c0bfbf562957072c1e0beb"
dependencies = [
"prost 0.12.6",
"prost-types 0.12.6",
"tendermint-proto 0.37.0",
"prost",
"prost-types",
"tendermint-proto",
"tonic",
]
[[package]]
name = "cosmrs"
version = "0.17.0"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85efbd5a3487e52ccd77897ef8f97b077a015953b46623fabc939f39d9c501fc"
checksum = "9f21bb63ec6a903510a3d01f44735dd4914724e5eccbe409e6da6833d17c7829"
dependencies = [
"cosmos-sdk-proto",
"ecdsa",
@ -732,7 +733,7 @@ dependencies = [
"serde_json",
"signature",
"subtle-encoding",
"tendermint 0.37.0",
"tendermint",
"thiserror",
]
@ -946,14 +947,17 @@ dependencies = [
[[package]]
name = "cw-client"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"anyhow",
"async-trait",
"cosmos-sdk-proto",
"cosmrs",
"hex",
"reqwest 0.12.8",
"serde",
"serde_json",
"tendermint",
"tonic",
]
[[package]]
@ -969,7 +973,7 @@ dependencies = [
"cw-utils",
"derivative",
"itertools 0.13.0",
"prost 0.13.3",
"prost",
"schemars",
"serde",
"sha2 0.10.8",
@ -1518,7 +1522,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.5.0",
"indexmap 2.6.0",
"slab",
"tokio",
"tokio-util",
@ -1537,7 +1541,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.1.0",
"indexmap 2.5.0",
"indexmap 2.6.0",
"slab",
"tokio",
"tokio-util",
@ -1585,6 +1589,12 @@ dependencies = [
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]]
name = "heck"
version = "0.5.0"
@ -1843,7 +1853,7 @@ dependencies = [
"blake3",
"bytes",
"hex",
"prost 0.13.3",
"prost",
"ripemd",
"sha2 0.10.8",
"sha3",
@ -1883,12 +1893,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
"hashbrown 0.15.0",
]
[[package]]
@ -1915,15 +1925,6 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
@ -2462,7 +2463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
"indexmap 2.5.0",
"indexmap 2.6.0",
]
[[package]]
@ -2574,16 +2575,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
dependencies = [
"bytes",
"prost-derive 0.12.6",
]
[[package]]
name = "prost"
version = "0.13.3"
@ -2591,7 +2582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
dependencies = [
"bytes",
"prost-derive 0.13.3",
"prost-derive",
]
[[package]]
@ -2608,26 +2599,13 @@ dependencies = [
"once_cell",
"petgraph",
"prettyplease",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"regex",
"syn 2.0.79",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
dependencies = [
"anyhow",
"itertools 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.79",
]
[[package]]
name = "prost-derive"
version = "0.13.3"
@ -2641,22 +2619,13 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "prost-types"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
dependencies = [
"prost 0.12.6",
]
[[package]]
name = "prost-types"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
dependencies = [
"prost 0.13.3",
"prost",
]
[[package]]
@ -2676,14 +2645,14 @@ dependencies = [
"futures-util",
"hex",
"k256",
"prost 0.13.3",
"prost",
"quartz-common",
"quartz-tm-prover",
"reqwest 0.12.8",
"serde",
"serde_json",
"sha2 0.10.8",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-rpc",
"thiserror",
@ -2697,7 +2666,6 @@ dependencies = [
[[package]]
name = "quartz-common"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"quartz-contract-core",
"quartz-enclave-core",
@ -2707,7 +2675,6 @@ dependencies = [
[[package]]
name = "quartz-contract-core"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"ciborium",
"cosmwasm-schema",
@ -2728,23 +2695,21 @@ dependencies = [
[[package]]
name = "quartz-cw-proof"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"clap",
"cosmrs",
"displaydoc",
"ics23",
"prost 0.13.3",
"prost",
"serde",
"serde_with",
"tendermint 0.38.1",
"tendermint",
"tendermint-rpc",
]
[[package]]
name = "quartz-dcap-verifier-msgs"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
@ -2753,7 +2718,6 @@ dependencies = [
[[package]]
name = "quartz-enclave-core"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"anyhow",
"async-trait",
@ -2776,7 +2740,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.8",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-rpc",
"thiserror",
@ -2789,9 +2753,8 @@ dependencies = [
[[package]]
name = "quartz-proto"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"prost 0.13.3",
"prost",
"tonic",
"tonic-build",
]
@ -2799,7 +2762,6 @@ dependencies = [
[[package]]
name = "quartz-tcbinfo-msgs"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"cosmwasm-schema",
]
@ -2807,7 +2769,6 @@ dependencies = [
[[package]]
name = "quartz-tee-ra"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
@ -2827,7 +2788,6 @@ dependencies = [
[[package]]
name = "quartz-tm-prover"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"clap",
"color-eyre",
@ -2836,7 +2796,7 @@ dependencies = [
"quartz-cw-proof",
"serde",
"serde_json",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-light-client-detector",
"tendermint-rpc",
@ -2848,10 +2808,9 @@ dependencies = [
[[package]]
name = "quartz-tm-stateless-verifier"
version = "0.1.0"
source = "git+ssh://git@github.com/informalsystems/cycles-quartz.git#e5004076813b8c98e7c385cc967372c38dd9ab4b"
dependencies = [
"displaydoc",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
]
@ -3789,37 +3748,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "tendermint"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "954496fbc9716eb4446cdd6d00c071a3e2f22578d62aa03b40c7e5b4fda3ed42"
dependencies = [
"bytes",
"digest 0.10.7",
"ed25519",
"ed25519-consensus",
"flex-error",
"futures",
"k256",
"num-traits",
"once_cell",
"prost 0.12.6",
"prost-types 0.12.6",
"ripemd",
"serde",
"serde_bytes",
"serde_json",
"serde_repr",
"sha2 0.10.8",
"signature",
"subtle",
"subtle-encoding",
"tendermint-proto 0.37.0",
"time",
"zeroize",
]
[[package]]
name = "tendermint"
version = "0.38.1"
@ -3832,10 +3760,12 @@ dependencies = [
"ed25519-consensus",
"flex-error",
"futures",
"k256",
"num-traits",
"once_cell",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"ripemd",
"serde",
"serde_bytes",
"serde_json",
@ -3844,7 +3774,7 @@ dependencies = [
"signature",
"subtle",
"subtle-encoding",
"tendermint-proto 0.38.1",
"tendermint-proto",
"time",
"zeroize",
]
@ -3858,7 +3788,7 @@ dependencies = [
"flex-error",
"serde",
"serde_json",
"tendermint 0.38.1",
"tendermint",
"toml",
"url",
]
@ -3880,7 +3810,7 @@ dependencies = [
"serde_derive",
"serde_json",
"static_assertions",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client-verifier",
"tendermint-rpc",
"time",
@ -3903,9 +3833,9 @@ dependencies = [
"serde_derive",
"serde_json",
"static_assertions",
"tendermint 0.38.1",
"tendermint",
"tendermint-light-client",
"tendermint-proto 0.38.1",
"tendermint-proto",
"tendermint-rpc",
"time",
"tracing",
@ -3920,23 +3850,7 @@ dependencies = [
"derive_more 0.99.18",
"flex-error",
"serde",
"tendermint 0.38.1",
"time",
]
[[package]]
name = "tendermint-proto"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc87024548c7f3da479885201e3da20ef29e85a3b13d04606b380ac4c7120d87"
dependencies = [
"bytes",
"flex-error",
"prost 0.12.6",
"prost-types 0.12.6",
"serde",
"serde_bytes",
"subtle-encoding",
"tendermint",
"time",
]
@ -3948,8 +3862,8 @@ checksum = "8ed14abe3b0502a3afe21ca74ca5cdd6c7e8d326d982c26f98a394445eb31d6e"
dependencies = [
"bytes",
"flex-error",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"serde",
"serde_bytes",
"subtle-encoding",
@ -3978,9 +3892,9 @@ dependencies = [
"serde_json",
"subtle",
"subtle-encoding",
"tendermint 0.38.1",
"tendermint",
"tendermint-config",
"tendermint-proto 0.38.1",
"tendermint-proto",
"thiserror",
"time",
"tokio",
@ -4186,7 +4100,7 @@ version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap 2.5.0",
"indexmap 2.6.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -4213,7 +4127,7 @@ dependencies = [
"hyper-util",
"percent-encoding",
"pin-project",
"prost 0.13.3",
"prost",
"socket2",
"tokio",
"tokio-stream",
@ -4232,7 +4146,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
"prost-types 0.13.3",
"prost-types",
"quote",
"syn 2.0.79",
]

View file

@ -52,7 +52,7 @@ tracing = "0.1.39"
futures-util = "0.3.30"
# cosmos
cosmrs = { version = "0.17.0", default-features = false }
cosmrs = { version = "0.18.0", default-features = false }
cosmwasm-std = { version = "2.1.1", default-features = false, features = [
"std",
] }

View file

@ -1,6 +1,6 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.out_dir("src/prost")
.compile(&["proto/transfers.proto"], &["proto"])?;
.compile_protos(&["proto/transfers.proto"], &["proto"])?;
Ok(())
}

View file

@ -5,7 +5,7 @@ libos.entrypoint = "{{ quartz_dir }}/target/release/quartz-app-transfers-enclave
loader.log_level = "{{ log_level }}"
loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"
loader.env.LD_LIBRARY_PATH = "/lib:/usr/local/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}"
loader.env.HOME = "{{ home }}"
loader.env.INSIDE_SGX = "1"
loader.env.TLS = { passthrough = true }
@ -19,13 +19,17 @@ loader.env.RA_TLS_ISV_PROD_ID = { passthrough = true }
loader.env.RA_TLS_EPID_API_KEY = { passthrough = true }
loader.env.MYAPP_DATA = { passthrough = true }
loader.env.QUARTZ_PORT = { passthrough = true }
loader.env.SSL_CERT_FILE = { passthrough = true }
loader.insecure__use_host_env = true
loader.argv = ["quartz-app-transfers-enclave",
"--chain-id", "testing",
"--chain-id", "{{ chain_id }}",
"--fmspc", "{{ fmspc }}",
"--tcbinfo-contract", "{{ tcbinfo_contract }}",
"--dcap-verifier-contract", "{{ dcap_verifier_contract }}",
"--node-url", "{{ node_url }}",
"--ws-url", "{{ ws_url }}",
"--grpc-url", "{{ grpc_url }}",
"--rpc-addr", "0.0.0.0:11090",
"--trusted-height", "{{ trusted_height }}",
"--trusted-hash", "{{ trusted_hash }}"]
@ -35,11 +39,14 @@ fs.mounts = [
{ uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" },
{ uri = "file:/usr/{{ arch_libdir }}", path = "/usr{{ arch_libdir }}" },
{ uri = "file:{{ quartz_dir }}", path = "{{ quartz_dir }}" },
{ uri = "file:{{ quartz_dir }}/../../crates/utils/cw-client/data/admin.sk", path = "/admin.sk" },
{ uri = "file:/etc/ssl/certs/ca-certificates.crt", path = "/etc/ssl/certs/ca-certificates.crt" },
{ uri = "file:/usr/lib/ssl/cert.pem", path = "/usr/lib/ssl/cert.pem" },
]
# sgx.debug = true
sgx.enclave_size = "512M"
sgx.max_threads = 4
sgx.max_threads = 16
sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
sgx.remote_attestation = "{{ ra_type }}"
@ -52,12 +59,24 @@ sgx.trusted_files = [
"file:{{ gramine.runtimedir() }}/",
"file:{{ arch_libdir }}/",
"file:/usr/{{ arch_libdir }}/",
"file:/etc/ssl/certs/ca-certificates.crt",
"file:{{ quartz_dir }}/../../crates/utils/cw-client/data/admin.sk",
]
sgx.allowed_files = [
"file:{{ quartz_dir }}/exchange.sk",
"file:{{ quartz_dir }}/request.json",
"file:admin.sk",
"file:/etc/nsswitch.conf",
"file:/etc/host.conf",
"file:/etc/ethers",
"file:/etc/hosts",
"file:/etc/group",
"file:/etc/passwd",
"file:/etc/gai.conf",
"file:/etc/ssl/certs/ca-certificates.crt",
"file:/usr/lib/ssl/cert.pem",
"file:/etc/sgx_default_qcnl.conf",
]
sys.insecure__allow_eventfd = true
sys.enable_sigterm_injection = true
sys.enable_extra_runtime_domain_names_conf = true

View file

@ -1,9 +1,10 @@
use std::{env, net::SocketAddr};
use std::{env, net::SocketAddr, path::PathBuf};
use clap::Parser;
use color_eyre::eyre::{eyre, Result};
use cosmrs::AccountId;
use quartz_common::enclave::types::Fmspc;
use reqwest::Url;
use tendermint::Hash;
use tendermint_light_client::types::{Height, TrustThreshold};
@ -64,11 +65,20 @@ pub struct Cli {
#[clap(long, default_value = "5")]
pub max_block_lag: u64,
#[clap(long, default_value = "127.0.0.1:11090")]
pub node_url: String,
#[clap(long, default_value = "http://127.0.0.1:26657")]
pub node_url: Url,
#[clap(long, default_value = "ws://127.0.0.1/websocket")]
pub ws_url: Url,
#[clap(long, default_value = "http://127.0.0.1:9090")]
pub grpc_url: Url,
#[clap(long, default_value = "admin")]
pub tx_sender: String,
#[clap(long, default_value = "admin.sk")]
pub sk_file: PathBuf,
}
fn default_rpc_addr() -> SocketAddr {

View file

@ -32,8 +32,9 @@ use quartz_common::{
server::{QuartzServer, WsListenerConfig},
},
};
use transfers_server::{TransfersOp, TransfersService};
use tokio::sync::mpsc;
use transfers_server::{TransfersOp, TransfersService};
use crate::wslistener::WsListener;
#[tokio::main(flavor = "current_thread")]
@ -73,10 +74,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ws_config = WsListenerConfig {
node_url: args.node_url,
ws_url: args.ws_url,
grpc_url: args.grpc_url,
tx_sender: args.tx_sender,
trusted_hash: args.trusted_hash,
trusted_height: args.trusted_height,
chain_id: args.chain_id,
sk_file: args.sk_file,
};
let sk = Arc::new(Mutex::new(None));

View file

@ -21,7 +21,13 @@ pub struct QueryResponse {
}
/// Generated client implementations.
pub mod settlement_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
#![allow(
unused_variables,
dead_code,
missing_docs,
clippy::wildcard_imports,
clippy::let_unit_value,
)]
use tonic::codegen::*;
use tonic::codegen::http::Uri;
#[derive(Debug, Clone)]
@ -43,8 +49,8 @@ pub mod settlement_client {
where
T: tonic::client::GrpcService<tonic::body::BoxBody>,
T::Error: Into<StdError>,
T::ResponseBody: Body<Data = Bytes> + Send + 'static,
<T::ResponseBody as Body>::Error: Into<StdError> + Send,
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
{
pub fn new(inner: T) -> Self {
let inner = tonic::client::Grpc::new(inner);
@ -69,7 +75,7 @@ pub mod settlement_client {
>,
<T as tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
>>::Error: Into<StdError> + Send + Sync,
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
{
SettlementClient::new(InterceptedService::new(inner, interceptor))
}
@ -112,8 +118,7 @@ pub mod settlement_client {
.ready()
.await
.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
tonic::Status::unknown(
format!("Service was not ready: {}", e.into()),
)
})?;
@ -131,8 +136,7 @@ pub mod settlement_client {
.ready()
.await
.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
tonic::Status::unknown(
format!("Service was not ready: {}", e.into()),
)
})?;
@ -149,11 +153,17 @@ pub mod settlement_client {
}
/// Generated server implementations.
pub mod settlement_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
#![allow(
unused_variables,
dead_code,
missing_docs,
clippy::wildcard_imports,
clippy::let_unit_value,
)]
use tonic::codegen::*;
/// Generated trait containing gRPC methods that should be implemented for use with SettlementServer.
#[async_trait]
pub trait Settlement: Send + Sync + 'static {
pub trait Settlement: std::marker::Send + std::marker::Sync + 'static {
async fn run(
&self,
request: tonic::Request<super::UpdateRequest>,
@ -164,14 +174,14 @@ pub mod settlement_server {
) -> std::result::Result<tonic::Response<super::QueryResponse>, tonic::Status>;
}
#[derive(Debug)]
pub struct SettlementServer<T: Settlement> {
pub struct SettlementServer<T> {
inner: Arc<T>,
accept_compression_encodings: EnabledCompressionEncodings,
send_compression_encodings: EnabledCompressionEncodings,
max_decoding_message_size: Option<usize>,
max_encoding_message_size: Option<usize>,
}
impl<T: Settlement> SettlementServer<T> {
impl<T> SettlementServer<T> {
pub fn new(inner: T) -> Self {
Self::from_arc(Arc::new(inner))
}
@ -225,8 +235,8 @@ pub mod settlement_server {
impl<T, B> tonic::codegen::Service<http::Request<B>> for SettlementServer<T>
where
T: Settlement,
B: Body + Send + 'static,
B::Error: Into<StdError> + Send + 'static,
B: Body + std::marker::Send + 'static,
B::Error: Into<StdError> + std::marker::Send + 'static,
{
type Response = http::Response<tonic::body::BoxBody>;
type Error = std::convert::Infallible;
@ -327,23 +337,25 @@ pub mod settlement_server {
}
_ => {
Box::pin(async move {
Ok(
http::Response::builder()
.status(200)
.header("grpc-status", tonic::Code::Unimplemented as i32)
.header(
http::header::CONTENT_TYPE,
tonic::metadata::GRPC_CONTENT_TYPE,
)
.body(empty_body())
.unwrap(),
)
let mut response = http::Response::new(empty_body());
let headers = response.headers_mut();
headers
.insert(
tonic::Status::GRPC_STATUS,
(tonic::Code::Unimplemented as i32).into(),
);
headers
.insert(
http::header::CONTENT_TYPE,
tonic::metadata::GRPC_CONTENT_TYPE,
);
Ok(response)
})
}
}
}
}
impl<T: Settlement> Clone for SettlementServer<T> {
impl<T> Clone for SettlementServer<T> {
fn clone(&self) -> Self {
let inner = self.inner.clone();
Self {
@ -355,7 +367,9 @@ pub mod settlement_server {
}
}
}
impl<T: Settlement> tonic::server::NamedService for SettlementServer<T> {
const NAME: &'static str = "transfers.Settlement";
/// Generated gRPC service name
pub const SERVICE_NAME: &str = "transfers.Settlement";
impl<T> tonic::server::NamedService for SettlementServer<T> {
const NAME: &'static str = SERVICE_NAME;
}
}

View file

@ -19,9 +19,9 @@ use quartz_common::{
};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use tokio::sync::mpsc::Sender;
use tonic::{Request, Response, Result as TonicResult, Status};
use transfers_contract::msg::execute::{ClearTextTransferRequestMsg, Request as TransfersRequest};
use tokio::sync::mpsc::Sender;
use crate::{
proto::{
@ -113,7 +113,7 @@ pub enum TransfersOpEvent {
pub struct TransfersOp<A: Attestor> {
pub client: TransfersService<A>,
pub event: TransfersOpEvent,
pub config: WsListenerConfig
pub config: WsListenerConfig,
}
#[derive(Clone, Debug)]
@ -121,14 +121,19 @@ pub struct TransfersService<A: Attestor> {
config: Config,
sk: Arc<Mutex<Option<SigningKey>>>,
attestor: A,
pub queue_producer: Sender<TransfersOp<A>>
pub queue_producer: Sender<TransfersOp<A>>,
}
impl<A> TransfersService<A>
where
A: Attestor,
{
pub fn new(config: Config, sk: Arc<Mutex<Option<SigningKey>>>, attestor: A, queue_producer: Sender<TransfersOp<A>>) -> Self {
pub fn new(
config: Config,
sk: Arc<Mutex<Option<SigningKey>>>,
attestor: A,
queue_producer: Sender<TransfersOp<A>>,
) -> Self {
Self {
config,
sk,

View file

@ -1,23 +1,21 @@
//TODO: get rid of this
use std::{collections::BTreeMap, str::FromStr};
use anyhow::{anyhow, Error, Result};
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
use cosmwasm_std::{Addr, HexBinary};
use cw_client::{CwClient, GrpcClient};
use futures_util::StreamExt;
use quartz_common::{
contract::msg::execute::attested::{
MockAttestation, RawAttested, RawAttestedMsgSansHandler, RawMockAttestation,
},
contract::msg::execute::attested::{RawAttested, RawAttestedMsgSansHandler},
enclave::{
attestor::Attestor,
server::{WebSocketHandler, WsListenerConfig},
},
};
use reqwest::Url;
use quartz_tm_prover::{config::Config as TmProverConfig, prover::prove};
use serde::Deserialize;
use serde_json::json;
use tendermint_rpc::{event::Event, query::EventType, SubscriptionClient, WebSocketClient};
use quartz_tm_prover::{config::Config as TmProverConfig, prover::prove};
use tonic::Request;
use tracing::info;
use transfers_contract::msg::{
@ -25,13 +23,11 @@ use transfers_contract::msg::{
AttestedMsg, ExecuteMsg,
QueryMsg::{GetRequests, GetState},
};
use cw_client::{CliWasmdClient, QueryResult, WasmdClient};
use crate::{
proto::{settlement_server::Settlement, QueryRequest, UpdateRequest},
transfers_server::{
QueryRequestMessage, TransfersOp, TransfersOpEvent,
TransfersService, UpdateRequestMessage,
QueryRequestMessage, TransfersOp, TransfersOpEvent, TransfersService, UpdateRequestMessage,
},
};
@ -46,23 +42,26 @@ impl TryFrom<Event> for TransfersOpEvent {
fn try_from(event: Event) -> Result<Self, Error> {
if let Some(events) = &event.events {
for (key, _) in events {
for key in events.keys() {
match key.as_str() {
k if k.starts_with("wasm-query_balance") => {
let (contract_address, ephemeral_pubkey, sender) =
extract_event_info(TransfersOpEventTypes::Query, &events)
.map_err(|_| anyhow!("Failed to extract event info from query event"))?;
extract_event_info(TransfersOpEventTypes::Query, events).map_err(
|_| anyhow!("Failed to extract event info from query event"),
)?;
return Ok(TransfersOpEvent::Query {
contract_address,
ephemeral_pubkey: ephemeral_pubkey.ok_or(anyhow!("Missing ephemeral_pubkey"))?,
ephemeral_pubkey: ephemeral_pubkey
.ok_or(anyhow!("Missing ephemeral_pubkey"))?,
sender: sender.ok_or(anyhow!("Missing sender"))?,
});
}
k if k.starts_with("wasm-transfer.action") => {
let (contract_address, _, _) =
extract_event_info(TransfersOpEventTypes::Transfer, &events)
.map_err(|_| anyhow!("Failed to extract event info from transfer event"))?;
extract_event_info(TransfersOpEventTypes::Transfer, events).map_err(
|_| anyhow!("Failed to extract event info from transfer event"),
)?;
return Ok(TransfersOpEvent::Transfer { contract_address });
}
@ -99,7 +98,11 @@ pub trait WsListener: Send + Sync + 'static {
}
#[async_trait::async_trait]
impl<A: Attestor> WsListener for TransfersService<A> {
impl<A> WsListener for TransfersService<A>
where
A: Attestor,
A::RawAttestation: for<'de> Deserialize<'de> + Send,
{
async fn process(&self, event: TransfersOpEvent, config: WsListenerConfig) -> Result<()> {
match event {
TransfersOpEvent::Transfer { contract_address } => {
@ -116,9 +119,8 @@ impl<A: Attestor> WsListener for TransfersService<A> {
}
}
let wsurl = format!("ws://{}/websocket", config.node_url);
// Wait some blocks to make sure transaction was confirmed
two_block_waitoor(&wsurl).await?;
two_block_waitoor(config.ws_url.as_str()).await?;
Ok(())
}
@ -141,58 +143,57 @@ fn extract_event_info(
.map_err(|e| anyhow!("Failed to parse contract address: {}", e))?;
// Set info for specific events
match op_event {
TransfersOpEventTypes::Query => {
sender = events
.get("message.sender")
.ok_or_else(|| anyhow!("Missing message.sender in events"))?
.first()
.cloned();
if let TransfersOpEventTypes::Query = op_event {
sender = events
.get("message.sender")
.ok_or_else(|| anyhow!("Missing message.sender in events"))?
.first()
.cloned();
ephemeral_pubkey = events
.get("wasm-query_balance.emphemeral_pubkey")
.ok_or_else(|| anyhow!("Missing wasm-query_balance.emphemeral_pubkey in events"))?
.first()
.cloned();
}
_ => {}
ephemeral_pubkey = events
.get("wasm-query_balance.emphemeral_pubkey")
.ok_or_else(|| anyhow!("Missing wasm-query_balance.emphemeral_pubkey in events"))?
.first()
.cloned();
}
Ok((contract_address, ephemeral_pubkey, sender))
}
async fn transfer_handler<A: Attestor>(
async fn transfer_handler<A>(
client: &TransfersService<A>,
contract: &AccountId,
ws_config: &WsListenerConfig,
) -> Result<()> {
) -> Result<()>
where
A: Attestor,
A::RawAttestation: for<'de> Deserialize<'de>,
{
let chain_id = &ChainId::from_str(&ws_config.chain_id)?;
let httpurl = Url::parse(&format!("http://{}", ws_config.node_url))?;
let cw_client = CliWasmdClient::new(httpurl.clone());
let cw_client = GrpcClient::new(ws_config.sk_file.clone(), ws_config.grpc_url.clone());
// Query contract state
let resp: QueryResult<Vec<TransferRequest>> = cw_client
let requests: Vec<TransferRequest> = cw_client
.query_smart(contract, json!(GetRequests {}))
.await
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?;
let requests = resp.data;
let resp: QueryResult<HexBinary> = cw_client
let state: HexBinary = cw_client
.query_smart(contract, json!(GetState {}))
.await
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?;
let state = resp.data;
// Request body contents
let update_contents = UpdateRequestMessage { state, requests };
// Wait 2 blocks
info!("Waiting 2 blocks for light client proof");
let wsurl = format!("ws://{}/websocket", ws_config.node_url);
two_block_waitoor(&wsurl).await?;
two_block_waitoor(ws_config.ws_url.as_str()).await?;
// Call tm prover with trusted hash and height
let prover_config = TmProverConfig {
primary: httpurl.as_str().parse()?,
witnesses: httpurl.as_str().parse()?,
primary: ws_config.node_url.as_str().parse()?,
witnesses: ws_config.node_url.as_str().parse()?,
trusted_height: ws_config.trusted_height,
trusted_hash: ws_config.trusted_hash,
verbose: "1".parse()?, // TODO: both tm-prover and cli define the same Verbosity struct. Need to define this once and import
@ -230,53 +231,52 @@ async fn transfer_handler<A: Attestor>(
.into_inner();
// Extract json from enclave response
let attested: RawAttested<UpdateMsg, HexBinary> =
let attested: RawAttested<UpdateMsg, A::RawAttestation> =
serde_json::from_str(&update_response.message)
.map_err(|e| anyhow!("Error deserializing UpdateMsg from enclave: {}", e))?;
// Build on-chain response
// TODO add non-mock support
let transfer_msg = ExecuteMsg::Update::<RawMockAttestation>(AttestedMsg {
let transfer_msg = ExecuteMsg::Update(AttestedMsg {
msg: RawAttestedMsgSansHandler(attested.msg),
attestation: MockAttestation(
attested
.attestation
.as_slice()
.try_into()
.map_err(|_| anyhow!("slice with incorrect length"))?,
)
.into(),
attestation: attested.attestation,
});
// Post response to chain
let output = cw_client.tx_execute(
contract,
chain_id,
2000000,
&ws_config.tx_sender,
json!(transfer_msg),
)?;
let output = cw_client
.tx_execute(
contract,
chain_id,
2000000,
&ws_config.tx_sender,
json!(transfer_msg),
"11000untrn",
)
.await?;
println!("Output TX: {}", output);
Ok(())
}
async fn query_handler<A: Attestor>(
async fn query_handler<A>(
client: &TransfersService<A>,
contract: &AccountId,
msg_sender: &String,
pubkey: &String,
msg_sender: &str,
pubkey: &str,
ws_config: &WsListenerConfig,
) -> Result<()> {
) -> Result<()>
where
A: Attestor,
A::RawAttestation: for<'de> Deserialize<'de>,
{
let chain_id = &ChainId::from_str(&ws_config.chain_id)?;
let httpurl = Url::parse(&format!("http://{}", ws_config.node_url))?;
let cw_client = CliWasmdClient::new(httpurl);
let cw_client = GrpcClient::new(ws_config.sk_file.clone(), ws_config.grpc_url.clone());
// Query contract state
let resp: QueryResult<HexBinary> = cw_client
let state: HexBinary = cw_client
.query_smart(contract, json!(GetState {}))
.await
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?;
let state = resp.data;
// Build request
let update_contents = QueryRequestMessage {
@ -297,32 +297,28 @@ async fn query_handler<A: Attestor>(
.into_inner();
// Extract json from the enclave response
let attested: RawAttested<QueryResponseMsg, HexBinary> =
let attested: RawAttested<QueryResponseMsg, A::RawAttestation> =
serde_json::from_str(&query_response.message)
.map_err(|e| anyhow!("Error deserializing QueryResponseMsg from enclave: {}", e))?;
// Build on-chain response
// TODO add non-mock support
let query_msg = ExecuteMsg::QueryResponse::<RawMockAttestation>(AttestedMsg {
let query_msg = ExecuteMsg::QueryResponse(AttestedMsg {
msg: RawAttestedMsgSansHandler(attested.msg),
attestation: MockAttestation(
attested
.attestation
.as_slice()
.try_into()
.map_err(|_| anyhow!("slice with incorrect length"))?,
)
.into(),
attestation: attested.attestation,
});
// Post response to chain
let output = cw_client.tx_execute(
contract,
chain_id,
2000000,
&ws_config.tx_sender,
json!(query_msg),
)?;
let output = cw_client
.tx_execute(
contract,
chain_id,
2000000,
&ws_config.tx_sender,
json!(query_msg),
"11000untrn",
)
.await?;
println!("Output TX: {}", output);
Ok(())

View file

@ -2,6 +2,7 @@ import { ChainInfo } from '@keplr-wallet/types'
import { localWasm } from './chains/localWasm'
import { localNeutron } from './chains/localNeutron'
import { testnetNeutron } from './chains/testnetNeutron'
const supportedChains: Record<string, ChainInfo> = {
doWasm: {
@ -12,6 +13,7 @@ const supportedChains: Record<string, ChainInfo> = {
},
localNeutron,
localWasm,
testnetNeutron,
}
const chain = supportedChains[process.env.NEXT_PUBLIC_TARGET_CHAIN!]

View file

@ -2,7 +2,7 @@ import { ChainInfo } from '@keplr-wallet/types'
// Neutron local chain definition
export const localNeutron: ChainInfo = {
chainId: 'testing',
chainId: 'test-1',
chainName: 'Local Neutron Testchain',
rpc: 'http://localhost:26657',
rest: 'http://localhost:1317',

View file

@ -0,0 +1,43 @@
import { ChainInfo } from '@keplr-wallet/types'
// Neutron testnet chain definition
export const testnetNeutron: ChainInfo = {
chainId: 'pion-1',
chainName: 'Neutron Testnet',
rpc: 'https://rpc-falcron.pion-1.ntrn.tech',
rest: 'https://rest-falcron.pion-1.ntrn.tech',
stakeCurrency: {
coinDenom: 'NEUTRON',
coinMinimalDenom: 'untrn',
coinDecimals: 6,
coinGeckoId: 'neutron',
},
bip44: {
coinType: 118,
},
bech32Config: {
bech32PrefixAccAddr: 'neutron',
bech32PrefixAccPub: 'neutron' + 'pub',
bech32PrefixValAddr: 'neutron' + 'valoper',
bech32PrefixValPub: 'neutron' + 'valoperpub',
bech32PrefixConsAddr: 'neutron' + 'valcons',
bech32PrefixConsPub: 'neutron' + 'valconspub',
},
currencies: [
{
coinDenom: 'NTRN',
coinMinimalDenom: 'untrn',
coinDecimals: 6,
coinGeckoId: 'neutron',
},
],
feeCurrencies: [
{
coinDenom: 'NTRN',
coinMinimalDenom: 'untrn',
coinDecimals: 6,
coinGeckoId: 'neutron',
gasPriceStep: { low: 0.001, average: 0.0025, high: 0.004 },
},
],
}

View file

@ -0,0 +1,14 @@
mock_sgx = false
tx_sender = "val1"
chain_id = "pion-1"
node_url = "https://rpc-falcron.pion-1.ntrn.tech"
ws_url = "wss://rpc-falcron.pion-1.ntrn.tech/websocket"
grpc_url = "https://grpc-falcron.pion-1.ntrn.tech:80"
# node_url = "https://neutron-testnet-rpc.polkachu.com"
# ws_url = "wss://neutron-testnet-rpc.polkachu.com/websocket"
# grpc_url = "https://grpc.baryon.remedy.tm.p2p.org:443"
enclave_rpc_addr = "https://127.0.0.1"
enclave_rpc_port = 11091
trusted_hash = ""
trusted_height = 0
release = true

View file

@ -1,7 +1,9 @@
mock_sgx = true
tx_sender = "admin"
chain_id = "testing"
node_url = "127.0.0.1:26657"
node_url = "http://127.0.0.1:26657"
ws_url = "ws://127.0.0.1/wesocket"
grpc_url = "http://127.0.0.1:9090"
enclave_rpc_addr = "http://127.0.0.1"
enclave_rpc_port = 11090
trusted_hash = ""