feat: move proxy logic into enclave (#174)

This commit is contained in:
Daniel Gushchyan 2024-09-18 13:04:33 -07:00 committed by GitHub
parent 628d7b4596
commit 69c1f63114
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1791 additions and 141 deletions

33
Cargo.lock generated
View file

@ -144,6 +144,9 @@ name = "anyhow"
version = "1.0.88" version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356"
dependencies = [
"backtrace",
]
[[package]] [[package]]
name = "anymap2" name = "anymap2"
@ -462,7 +465,7 @@ dependencies = [
"rustversion", "rustversion",
"serde", "serde",
"sync_wrapper 1.0.1", "sync_wrapper 1.0.1",
"tower", "tower 0.4.13",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
] ]
@ -2656,7 +2659,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
"tower", "tower 0.4.13",
"tower-service", "tower-service",
"tracing", "tracing",
] ]
@ -3316,6 +3319,9 @@ dependencies = [
name = "mtcs-enclave" name = "mtcs-enclave"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"async-trait",
"base64 0.22.1",
"clap", "clap",
"color-eyre", "color-eyre",
"cosmrs", "cosmrs",
@ -3323,20 +3329,24 @@ dependencies = [
"cw-multi-test", "cw-multi-test",
"cw-tee-mtcs", "cw-tee-mtcs",
"ecies", "ecies",
"futures-util",
"hex", "hex",
"k256", "k256",
"mtcs", "mtcs",
"prost 0.13.2", "prost 0.13.2",
"quartz-common", "quartz-common",
"reqwest 0.12.7",
"serde", "serde",
"serde_json", "serde_json",
"tendermint 0.38.1", "tendermint 0.38.1",
"tendermint-light-client", "tendermint-light-client",
"tendermint-rpc",
"thiserror", "thiserror",
"tokio", "tokio",
"tonic", "tonic",
"tonic-build", "tonic-build",
"uuid", "uuid",
"wasmd-client",
] ]
[[package]] [[package]]
@ -4087,7 +4097,6 @@ dependencies = [
"hex", "hex",
"k256", "k256",
"miette", "miette",
"mtcs-enclave",
"once_cell", "once_cell",
"prost 0.13.2", "prost 0.13.2",
"quartz-common", "quartz-common",
@ -4147,12 +4156,15 @@ dependencies = [
name = "quartz-enclave" name = "quartz-enclave"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"async-trait",
"clap", "clap",
"color-eyre", "color-eyre",
"cosmrs", "cosmrs",
"cosmwasm-std", "cosmwasm-std",
"cw-proof", "cw-proof",
"ecies", "ecies",
"futures-util",
"hex", "hex",
"k256", "k256",
"mtcs", "mtcs",
@ -4165,9 +4177,12 @@ dependencies = [
"sha2 0.10.8", "sha2 0.10.8",
"tendermint 0.38.1", "tendermint 0.38.1",
"tendermint-light-client", "tendermint-light-client",
"tendermint-rpc",
"thiserror",
"tm-stateless-verifier", "tm-stateless-verifier",
"tokio", "tokio",
"tonic", "tonic",
"tower 0.5.1",
] ]
[[package]] [[package]]
@ -5750,7 +5765,7 @@ dependencies = [
"socket2", "socket2",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tower", "tower 0.4.13",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing", "tracing",
@ -5789,6 +5804,16 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
dependencies = [
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"

View file

@ -22,7 +22,7 @@ authors = ["Informal Systems <hello@informal.systems>"]
[workspace.dependencies] [workspace.dependencies]
# external # external
anyhow = { version = "1.0.86", default-features = false } anyhow = { version = "1.0.86", features = ["std", "backtrace"] }
async-trait = { version = "0.1.79", default-features = false } async-trait = { version = "0.1.79", default-features = false }
bip32 = { version = "0.5.1", default-features = false, features = ["alloc", "secp256k1", "bip39"] } bip32 = { version = "0.5.1", default-features = false, features = ["alloc", "secp256k1", "bip39"] }
cargo-generate = "0.21.3" cargo-generate = "0.21.3"
@ -32,6 +32,7 @@ der = { version = "0.7.9", default-features = false }
displaydoc = { version = "0.2.4", default-features = false } displaydoc = { version = "0.2.4", default-features = false }
ecies = { version = "0.2.3", default-features = false, features = ["pure"] } ecies = { version = "0.2.3", default-features = false, features = ["pure"] }
futures = { version = "0.3.27", default-features = false, features = ["alloc"] } futures = { version = "0.3.27", default-features = false, features = ["alloc"] }
futures-util = { version = "0.3.30" }
hex = { version = "0.4.3", default-features = false } hex = { version = "0.4.3", default-features = false }
hex-literal = { version = "0.4.1", default-features = false } hex-literal = { version = "0.4.1", default-features = false }
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] } k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
@ -50,6 +51,7 @@ thiserror = { version = "1.0.49", default-features = false }
tokio = { version = "=1.39.2", default-features = false, features = ["macros", "rt"] } tokio = { version = "=1.39.2", default-features = false, features = ["macros", "rt"] }
tonic = { version = "=0.12.1", default-features = false, features = ["codegen", "prost", "transport"] } tonic = { version = "=0.12.1", default-features = false, features = ["codegen", "prost", "transport"] }
tonic-build = { version = "=0.12.1", default-features = false, features = ["prost", "transport"] } tonic-build = { version = "=0.12.1", default-features = false, features = ["prost", "transport"] }
tower = { version = "0.5.0" }
tracing = { version = "0.1.39", default-features = false } tracing = { version = "0.1.39", default-features = false }
tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt"] } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt"] }
uuid = { version = "1.4.1", default-features = false, features = ["serde"] } uuid = { version = "1.4.1", default-features = false, features = ["serde"] }

View file

@ -20,6 +20,7 @@ mock-sgx = ["quartz-common/mock-sgx-cw", "quartz-common/mock-sgx-enclave"]
[dependencies] [dependencies]
# external # external
async-trait.workspace = true
clap.workspace = true clap.workspace = true
color-eyre.workspace = true color-eyre.workspace = true
ecies.workspace = true ecies.workspace = true
@ -32,12 +33,18 @@ thiserror.workspace = true
tokio.workspace = true tokio.workspace = true
tonic.workspace = true tonic.workspace = true
uuid.workspace = true uuid.workspace = true
futures-util.workspace = true
anyhow.workspace = true
base64 = "0.22.1"
reqwest.workspace = true
# cosmos # cosmos
cosmrs.workspace = true cosmrs.workspace = true
cosmwasm-std.workspace = true cosmwasm-std.workspace = true
tendermint.workspace = true tendermint.workspace = true
tendermint-light-client.workspace = true tendermint-light-client.workspace = true
tendermint-rpc.workspace = true
# quartz # quartz
cw-tee-mtcs.workspace = true cw-tee-mtcs.workspace = true
@ -45,6 +52,7 @@ mtcs.workspace = true
# quartz # quartz
quartz-common = { workspace = true, features = ["full"]} quartz-common = { workspace = true, features = ["full"]}
wasmd-client = { workspace = true }
[dev-dependencies] [dev-dependencies]
cw-multi-test = "2.1.0" cw-multi-test = "2.1.0"

View file

@ -2,7 +2,8 @@ use std::net::SocketAddr;
use clap::Parser; use clap::Parser;
use color_eyre::eyre::{eyre, Result}; use color_eyre::eyre::{eyre, Result};
use cosmrs::{tendermint::Hash, AccountId}; use cosmrs::AccountId;
use tendermint::Hash;
use tendermint_light_client::types::{Height, TrustThreshold}; use tendermint_light_client::types::{Height, TrustThreshold};
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> { fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
@ -53,4 +54,10 @@ pub struct Cli {
/// Maximum block lag, in seconds /// Maximum block lag, in seconds
#[clap(long, default_value = "5")] #[clap(long, default_value = "5")]
pub max_block_lag: u64, pub max_block_lag: u64,
#[clap(long, default_value = "127.0.0.1:11090")]
pub node_url: String,
#[clap(long, default_value = "admin")]
pub tx_sender: String,
} }

View file

@ -1,5 +1,5 @@
#![doc = include_str!("../README.md")]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
#![warn( #![warn(
clippy::checked_conversions, clippy::checked_conversions,
clippy::panic, clippy::panic,
@ -16,6 +16,7 @@ mod cli;
mod mtcs_server; mod mtcs_server;
mod proto; mod proto;
mod types; mod types;
mod wslistener;
use std::{ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -25,23 +26,20 @@ use std::{
use clap::Parser; use clap::Parser;
use cli::Cli; use cli::Cli;
use mtcs_server::MtcsService; use mtcs_server::MtcsService;
use proto::clearing_server::ClearingServer as MtcsServer;
use quartz_common::{ use quartz_common::{
contract::state::{Config, LightClientOpts}, contract::state::{Config, LightClientOpts},
enclave::{ enclave::{
attestor::{Attestor, DefaultAttestor}, attestor::{Attestor, DefaultAttestor},
server::CoreService, server::{QuartzServer, WsListenerConfig},
}, },
proto::core_server::CoreServer,
}; };
use tonic::transport::Server;
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Cli::parse(); let args = Cli::parse();
let light_client_opts = LightClientOpts::new( let light_client_opts = LightClientOpts::new(
args.chain_id, args.chain_id.clone(),
args.trusted_height.into(), args.trusted_height.into(),
Vec::from(args.trusted_hash) Vec::from(args.trusted_hash)
.try_into() .try_into()
@ -64,15 +62,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
args.tcbinfo_contract.map(|c| c.to_string()), args.tcbinfo_contract.map(|c| c.to_string()),
); );
let ws_config = WsListenerConfig {
node_url: args.node_url,
tx_sender: args.tx_sender,
trusted_hash: args.trusted_hash,
trusted_height: args.trusted_height,
chain_id: args.chain_id,
};
let sk = Arc::new(Mutex::new(None)); let sk = Arc::new(Mutex::new(None));
Server::builder() QuartzServer::new(config.clone(), sk.clone(), attestor.clone(), ws_config)
.add_service(CoreServer::new(CoreService::new( .add_service(MtcsService::new(config, sk, attestor))
config.clone(),
sk.clone(),
attestor.clone(),
)))
.add_service(MtcsServer::new(MtcsService::new(config, sk, attestor)))
.serve(args.rpc_addr) .serve(args.rpc_addr)
.await?; .await?;

View file

@ -4,7 +4,6 @@ use std::{
}; };
use cosmwasm_std::{Addr, HexBinary, Uint128}; use cosmwasm_std::{Addr, HexBinary, Uint128};
//TODO: get rid of this
use cw_tee_mtcs::{ use cw_tee_mtcs::{
msg::execute::SubmitSetoffsMsg, msg::execute::SubmitSetoffsMsg,
state::{LiquiditySource, LiquiditySourceType, RawHash, SettleOff, Transfer}, state::{LiquiditySource, LiquiditySourceType, RawHash, SettleOff, Transfer},
@ -17,18 +16,29 @@ use mtcs::{
}; };
use quartz_common::{ use quartz_common::{
contract::{msg::execute::attested::RawAttested, state::Config}, contract::{msg::execute::attested::RawAttested, state::Config},
enclave::{attestor::Attestor, server::ProofOfPublication}, enclave::{attestor::Attestor, server::IntoServer},
}; };
use tonic::{Request, Response, Result as TonicResult, Status}; use tonic::{Request, Response, Result as TonicResult, Status};
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse}, proto::{
clearing_server::{Clearing, ClearingServer},
RunClearingRequest, RunClearingResponse,
},
types::{ContractObligation, RunClearingMessage}, types::{ContractObligation, RunClearingMessage},
}; };
pub type RawCipherText = HexBinary; pub type RawCipherText = HexBinary;
impl<A: Attestor> IntoServer for MtcsService<A> {
type Server = ClearingServer<MtcsService<A>>;
fn into_server(self) -> Self::Server {
ClearingServer::new(self)
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MtcsService<A> { pub struct MtcsService<A> {
config: Config, // TODO: this config is not used anywhere config: Config, // TODO: this config is not used anywhere
@ -59,20 +69,20 @@ where
request: Request<RunClearingRequest>, request: Request<RunClearingRequest>,
) -> TonicResult<Response<RunClearingResponse>> { ) -> TonicResult<Response<RunClearingResponse>> {
// Light client check // Light client check
let message: ProofOfPublication<RunClearingMessage> = { let message: RunClearingMessage = {
let message = request.into_inner().message; let message = request.into_inner().message;
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))? serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?
}; };
let (proof_value, message) = message // let (proof_value, message) = message
.verify(self.config.light_client_opts()) // .verify(self.config.light_client_opts())
.map_err(Status::failed_precondition)?; // .map_err(Status::failed_precondition)?;
let proof_value_matches_msg = // let proof_value_matches_msg =
serde_json::to_string(&message.intents).is_ok_and(|s| s.as_bytes() == proof_value); // serde_json::to_string(&message.intents).is_ok_and(|s| s.as_bytes() == proof_value);
if !proof_value_matches_msg { // if !proof_value_matches_msg {
return Err(Status::failed_precondition("proof verification")); // return Err(Status::failed_precondition("proof verification"));
} // }
// TODO: ensure no duplicates somewhere else! // TODO: ensure no duplicates somewhere else!
let liquidity_sources: Vec<LiquiditySource> = let liquidity_sources: Vec<LiquiditySource> =
message.liquidity_sources.into_iter().collect(); message.liquidity_sources.into_iter().collect();

View file

@ -0,0 +1,231 @@
//TODO: get rid of this
use std::{collections::BTreeMap, str::FromStr};
use anyhow::{anyhow, Result};
use base64::prelude::*;
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
use cosmwasm_std::{HexBinary, Uint64};
use cw_tee_mtcs::msg::{
execute::SubmitSetoffsMsg, AttestedMsg, ExecuteMsg, GetLiquiditySourcesResponse,
QueryMsg::GetLiquiditySources,
};
use quartz_common::{
contract::msg::execute::attested::{
MockAttestation, RawAttested, RawAttestedMsgSansHandler, RawMockAttestation,
},
enclave::{
attestor::Attestor,
server::{WebSocketHandler, WsListenerConfig},
},
};
use reqwest::Url;
// use quartz_tee_ra::{intel_sgx::epid::types::ReportBody, IASReport};
use serde_json::json;
use tendermint_rpc::{event::Event, query::EventType};
use tonic::Request;
use wasmd_client::{CliWasmdClient, QueryResult, WasmdClient};
use crate::{
mtcs_server::MtcsService,
proto::{clearing_server::Clearing, RunClearingRequest},
types::RunClearingMessage,
};
// TODO: Need to prevent listener from taking actions until handshake is completed
#[async_trait::async_trait]
impl<A: Attestor> WebSocketHandler for MtcsService<A> {
async fn handle(&self, event: Event, config: WsListenerConfig) -> Result<()> {
// Validation
if !is_init_clearing_event(&event) {
return Ok(());
} else {
println!("Found clearing event");
let mut sender = None;
let mut contract_address = None;
if let Some(events) = &event.events {
for (key, values) in events {
match key.as_str() {
"message.sender" => {
sender = values.first().cloned();
}
"wasm._contract_address" => {
contract_address = values.first().cloned();
}
_ => {}
}
}
}
// TODO: add some checks based on event messages
if sender.is_none() || contract_address.is_none() {
return Ok(()); // TODO: change return type
}
handler(
self,
&contract_address
.expect("infallible")
.parse::<AccountId>()
.map_err(|e| anyhow!(e))?,
sender.expect("infallible"),
&config.node_url,
)
.await?;
}
Ok(())
}
}
fn is_init_clearing_event(event: &Event) -> bool {
// Check if the event is a transaction type
if let Some(EventType::Tx) = event.event_type() {
// Check for the "wasm.action" key with the value "init_clearing"
if let Some(events) = &event.events {
return events.iter().any(|(key, values)| {
key == "wasm.action" && values.contains(&"init_clearing".to_string())
});
}
}
false
}
async fn handler<A: Attestor>(
client: &MtcsService<A>,
contract: &AccountId,
sender: String,
node_url: &str,
) -> Result<()> {
let chain_id = &ChainId::from_str("testing")?;
let httpurl = Url::parse(&format!("http://{}", node_url))?;
let wasmd_client = CliWasmdClient::new(httpurl);
// Query obligations and liquidity sources from chain
let clearing_contents = query_chain(&wasmd_client, contract).await?;
// Send queried data to enclave over gRPC
let request = Request::new(RunClearingRequest {
message: json!(clearing_contents).to_string(),
});
let clearing_response = client
.run(request)
.await
.map_err(|e| anyhow!("Failed to communicate to relayer. {e}"))?
.into_inner();
// Extract json from the Protobuf message
let attested: RawAttested<SubmitSetoffsMsg, Vec<u8>> =
serde_json::from_str(&clearing_response.message)
.map_err(|e| anyhow!("Error serializing SubmitSetoffs: {}", e))?;
// TODO add non-mock support, get IAS report and build attested message
let msg = RawAttestedMsgSansHandler(attested.msg);
let setoffs_msg = ExecuteMsg::SubmitSetoffs::<RawMockAttestation>(AttestedMsg {
msg,
attestation: MockAttestation(attested.attestation.try_into().unwrap()).into(),
});
// Send setoffs to mtcs contract on chain
let output =
wasmd_client.tx_execute(contract, chain_id, 2000000, &sender, json!(setoffs_msg))?;
println!("Setoffs TX: {}", output);
Ok(())
}
// TODO: replace raw queries with smart
async fn query_chain(
wasmd_client: &CliWasmdClient,
contract: &AccountId,
) -> Result<RunClearingMessage> {
// Get epoch counter
let resp: QueryResult<String> = wasmd_client
.query_raw(contract, hex::encode("epoch_counter"))
.map_err(|e| anyhow!("Problem querying epoch: {}", e))?;
let mut epoch_counter: usize = String::from_utf8(BASE64_STANDARD.decode(resp.data)?)?
.trim_matches('"')
.parse::<usize>()?;
if epoch_counter > 1 {
epoch_counter -= 1;
}
// TODO: replace with tracer log here
// println!("epoch: {}", epoch_counter);
// Get obligations
let resp: QueryResult<String> = wasmd_client
.query_raw(
contract,
hex::encode(format!("{}/obligations", epoch_counter)),
)
.map_err(|e| anyhow!("Problem querying obligatons: {}", e))?;
let decoded_obligs = BASE64_STANDARD.decode(resp.data)?;
let obligations_map: BTreeMap<HexBinary, HexBinary> =
serde_json::from_slice(&decoded_obligs).unwrap_or_default();
// println!("obligations \n {:?}", obligations_map);
// TODO: replace with tracer log here
// Get liquidity sources
let resp: QueryResult<GetLiquiditySourcesResponse> = wasmd_client
.query_smart(
contract,
json!(GetLiquiditySources {
epoch: Some(Uint64::new(epoch_counter as u64))
}),
)
.map_err(|e| anyhow!("Problem querying liquidity sources: {}", e))?;
let liquidity_sources = resp.data.liquidity_sources;
// TODO: replace with tracer log here
// println!("liquidity_sources \n {:?}", liquidity_sources);
Ok(RunClearingMessage {
intents: obligations_map,
liquidity_sources: liquidity_sources.into_iter().collect(),
})
}
// Request the IAS report for EPID attestations
// async fn gramine_ias_request(
// attested_msg: Vec<u8>,
// user: &str,
// ) -> Result<EpidAttestation, anyhow::Error> {
// let ias_api_key = String::from("669244b3e6364b5888289a11d2a1726d");
// let ra_client_spid = String::from("51CAF5A48B450D624AEFE3286D314894");
// let quote_file = format!("/tmp/{}_test.quote", user);
// let report_file = format!("/tmp/{}_datareport", user);
// let report_sig_file = format!("/tmp/{}_datareportsig", user);
// // Write the binary data to a file
// let mut file = File::create(&quote_file).await?;
// file.write_all(&attested_msg)
// .await
// .map_err(|e| anyhow!("Couldn't write to file. {e}"))?;
// let mut gramine = Command::new("gramine-sgx-ias-request");
// let command = gramine
// .arg("report")
// .args(["-g", &ra_client_spid])
// .args(["-k", &ias_api_key])
// .args(["-q", &quote_file])
// .args(["-r", &report_file])
// .args(["-s", &report_sig_file]);
// let output = command.output()?;
// if !output.status.success() {
// return Err(anyhow!("Couldn't run gramine. {:?}", output));
// }
// let report: ReportBody = serde_json::from_str(&fs::read_to_string(report_file).await?)?;
// let report_sig_str = fs::read_to_string(report_sig_file).await?.replace('\r', "");
// let report_sig: Binary = Binary::from_base64(report_sig_str.trim())?;
// Ok(EpidAttestation::new(IASReport { report, report_sig }))
// }

View file

@ -44,6 +44,7 @@ cw20-base = { version = "2.0.0", default-features = false, features = ["library"
cw-utils = { version = "2.0.0", default-features = false } cw-utils = { version = "2.0.0", default-features = false }
# quartz # quartz
# quartz-common = { git = "ssh://git@github.com/informalsystems/cycles-quartz.git", features=["contract"]}
quartz-common = { path = "../../../core/quartz-common", features=["contract"]} quartz-common = { path = "../../../core/quartz-common", features=["contract"]}
# patch indirect deps # patch indirect deps

View file

@ -230,6 +230,8 @@ pub mod execute {
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> { pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg { match msg {
QueryMsg::GetBalance { address } => to_json_binary(&query::get_balance(deps, address)?), QueryMsg::GetBalance { address } => to_json_binary(&query::get_balance(deps, address)?),
QueryMsg::GetRequests {} => to_json_binary(&query::get_requests(deps)?),
QueryMsg::GetState {} => to_json_binary(&query::get_state(deps)?),
} }
} }
mod query { mod query {
@ -239,4 +241,12 @@ mod query {
let balance = BALANCES.may_load(deps.storage, &address)?; let balance = BALANCES.may_load(deps.storage, &address)?;
Ok(balance.unwrap_or_default()) Ok(balance.unwrap_or_default())
} }
pub fn get_requests(deps: Deps) -> StdResult<Vec<Request>> {
Ok(REQUESTS.load(deps.storage)?)
}
pub fn get_state(deps: Deps) -> StdResult<HexBinary> {
Ok(STATE.load(deps.storage)?)
}
} }

View file

@ -4,7 +4,7 @@ use quartz_common::contract::{
prelude::*, prelude::*,
}; };
type AttestedMsg<M, RA = RawDefaultAttestation> = RawAttested<RawAttestedMsgSansHandler<M>, RA>; pub type AttestedMsg<M, RA = RawDefaultAttestation> = RawAttested<RawAttestedMsgSansHandler<M>, RA>;
#[cw_serde] #[cw_serde]
pub struct InstantiateMsg<RA = RawDefaultAttestation> { pub struct InstantiateMsg<RA = RawDefaultAttestation> {
@ -15,6 +15,8 @@ pub struct InstantiateMsg<RA = RawDefaultAttestation> {
#[cw_serde] #[cw_serde]
pub enum QueryMsg { pub enum QueryMsg {
GetBalance { address: String }, GetBalance { address: String },
GetRequests {},
GetState {},
} }
#[cw_serde] #[cw_serde]

View file

@ -90,6 +90,9 @@ name = "anyhow"
version = "1.0.86" version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
dependencies = [
"backtrace",
]
[[package]] [[package]]
name = "ark-bls12-381" name = "ark-bls12-381"
@ -296,6 +299,23 @@ dependencies = [
"syn 2.0.76", "syn 2.0.76",
] ]
[[package]]
name = "async-tungstenite"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3609af4bbf701ddaf1f6bb4e6257dff4ff8932327d0e685d3f653724c258b1ac"
dependencies = [
"futures-io",
"futures-util",
"log",
"pin-project-lite",
"rustls-native-certs 0.7.3",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.25.0",
"tungstenite",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -330,7 +350,7 @@ dependencies = [
"rustversion", "rustversion",
"serde", "serde",
"sync_wrapper 1.0.1", "sync_wrapper 1.0.1",
"tower", "tower 0.4.13",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
] ]
@ -1290,6 +1310,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b"
dependencies = [ dependencies = [
"eyre",
"paste", "paste",
] ]
@ -1299,6 +1320,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -1344,6 +1380,17 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.76",
]
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.30" version = "0.3.30"
@ -1363,10 +1410,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-macro",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab",
] ]
[[package]] [[package]]
@ -1656,9 +1705,27 @@ dependencies = [
"futures-util", "futures-util",
"http 0.2.12", "http 0.2.12",
"hyper 0.14.30", "hyper 0.14.30",
"rustls", "rustls 0.21.12",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls 0.24.1",
]
[[package]]
name = "hyper-rustls"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http 1.1.0",
"hyper 1.4.1",
"hyper-util",
"rustls 0.23.12",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.0",
"tower-service",
"webpki-roots",
] ]
[[package]] [[package]]
@ -1674,6 +1741,22 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper 1.4.1",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.7" version = "0.1.7"
@ -1689,7 +1772,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
"tower", "tower 0.4.13",
"tower-service", "tower-service",
"tracing", "tracing",
] ]
@ -1910,6 +1993,15 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]] [[package]]
name = "matchit" name = "matchit"
version = "0.7.3" version = "0.7.3"
@ -2060,6 +2152,23 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]]
name = "native-tls"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -2179,12 +2288,50 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "openssl"
version = "0.10.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.76",
]
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.1.5" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "owo-colors" name = "owo-colors"
version = "3.5.0" version = "3.5.0"
@ -2336,6 +2483,12 @@ dependencies = [
"spki", "spki",
] ]
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]] [[package]]
name = "polyval" name = "polyval"
version = "0.6.2" version = "0.6.2"
@ -2481,26 +2634,34 @@ name = "quartz-app-transfers-enclave"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait",
"base64 0.22.1",
"clap", "clap",
"color-eyre", "color-eyre",
"cosmrs", "cosmrs",
"cosmwasm-std", "cosmwasm-std",
"cw-multi-test", "cw-multi-test",
"ecies", "ecies",
"futures-util",
"hex", "hex",
"k256", "k256",
"prost 0.13.1", "prost 0.13.1",
"quartz-common", "quartz-common",
"reqwest 0.12.7",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.8", "sha2 0.10.8",
"tendermint 0.38.1", "tendermint 0.38.1",
"tendermint-light-client", "tendermint-light-client",
"tendermint-rpc",
"thiserror", "thiserror",
"tm-prover",
"tokio", "tokio",
"tonic", "tonic",
"tonic-build", "tonic-build",
"tracing",
"transfers-contract", "transfers-contract",
"wasmd-client",
] ]
[[package]] [[package]]
@ -2534,12 +2695,15 @@ dependencies = [
name = "quartz-enclave" name = "quartz-enclave"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"async-trait",
"clap", "clap",
"color-eyre", "color-eyre",
"cosmrs", "cosmrs",
"cosmwasm-std", "cosmwasm-std",
"cw-proof", "cw-proof",
"ecies", "ecies",
"futures-util",
"hex", "hex",
"k256", "k256",
"mtcs", "mtcs",
@ -2552,9 +2716,12 @@ dependencies = [
"sha2 0.10.8", "sha2 0.10.8",
"tendermint 0.38.1", "tendermint 0.38.1",
"tendermint-light-client", "tendermint-light-client",
"tendermint-rpc",
"thiserror",
"tm-stateless-verifier", "tm-stateless-verifier",
"tokio", "tokio",
"tonic", "tonic",
"tower 0.5.1",
] ]
[[package]] [[package]]
@ -2589,6 +2756,54 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "quinn"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684"
dependencies = [
"bytes",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls 0.23.12",
"socket2",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "quinn-proto"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
dependencies = [
"bytes",
"rand",
"ring",
"rustc-hash",
"rustls 0.23.12",
"slab",
"thiserror",
"tinyvec",
"tracing",
]
[[package]]
name = "quinn-udp"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b"
dependencies = [
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.37" version = "1.0.37"
@ -2665,8 +2880,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata", "regex-automata 0.4.7",
"regex-syntax", "regex-syntax 0.8.4",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
] ]
[[package]] [[package]]
@ -2677,9 +2901,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax", "regex-syntax 0.8.4",
] ]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.4" version = "0.8.4"
@ -2701,7 +2931,7 @@ dependencies = [
"http 0.2.12", "http 0.2.12",
"http-body 0.4.6", "http-body 0.4.6",
"hyper 0.14.30", "hyper 0.14.30",
"hyper-rustls", "hyper-rustls 0.24.2",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
@ -2709,16 +2939,16 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls", "rustls 0.21.12",
"rustls-native-certs", "rustls-native-certs 0.6.3",
"rustls-pemfile", "rustls-pemfile 1.0.4",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper 0.1.2", "sync_wrapper 0.1.2",
"system-configuration", "system-configuration 0.5.1",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls 0.24.1",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
@ -2727,6 +2957,54 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "reqwest"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
dependencies = [
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.4.6",
"http 1.1.0",
"http-body 1.0.1",
"http-body-util",
"hyper 1.4.1",
"hyper-rustls 0.27.3",
"hyper-tls",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.12",
"rustls-pemfile 2.1.3",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.1",
"system-configuration 0.6.1",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.26.0",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"windows-registry",
]
[[package]] [[package]]
name = "rfc6979" name = "rfc6979"
version = "0.4.0" version = "0.4.0"
@ -2767,6 +3045,12 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
@ -2806,10 +3090,38 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [ dependencies = [
"log", "log",
"ring", "ring",
"rustls-webpki", "rustls-webpki 0.101.7",
"sct", "sct",
] ]
[[package]]
name = "rustls"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.7",
"subtle",
"zeroize",
]
[[package]]
name = "rustls"
version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.7",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "rustls-native-certs" name = "rustls-native-certs"
version = "0.6.3" version = "0.6.3"
@ -2817,7 +3129,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
dependencies = [ dependencies = [
"openssl-probe", "openssl-probe",
"rustls-pemfile", "rustls-pemfile 1.0.4",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-native-certs"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
dependencies = [
"openssl-probe",
"rustls-pemfile 2.1.3",
"rustls-pki-types",
"schannel", "schannel",
"security-framework", "security-framework",
] ]
@ -2831,6 +3156,22 @@ dependencies = [
"base64 0.21.7", "base64 0.21.7",
] ]
[[package]]
name = "rustls-pemfile"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
dependencies = [
"base64 0.22.1",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.101.7" version = "0.101.7"
@ -2841,6 +3182,17 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "rustls-webpki"
version = "0.102.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.17" version = "1.0.17"
@ -3085,6 +3437,17 @@ dependencies = [
"syn 2.0.76", "syn 2.0.76",
] ]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.10.7",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.9.9" version = "0.9.9"
@ -3119,6 +3482,15 @@ dependencies = [
"keccak", "keccak",
] ]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@ -3242,6 +3614,9 @@ name = "sync_wrapper"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "synstructure" name = "synstructure"
@ -3262,7 +3637,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"core-foundation", "core-foundation",
"system-configuration-sys", "system-configuration-sys 0.5.0",
]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"system-configuration-sys 0.6.0",
] ]
[[package]] [[package]]
@ -3275,6 +3661,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "tcbinfo" name = "tcbinfo"
version = "0.1.0" version = "0.1.0"
@ -3284,6 +3680,7 @@ dependencies = [
"cw-storage-plus", "cw-storage-plus",
"cw2", "cw2",
"der", "der",
"getrandom",
"hex", "hex",
"mc-attestation-verifier", "mc-attestation-verifier",
"p256", "p256",
@ -3403,6 +3800,30 @@ dependencies = [
"tendermint-light-client-verifier", "tendermint-light-client-verifier",
"tendermint-rpc", "tendermint-rpc",
"time", "time",
"tokio",
"tracing",
]
[[package]]
name = "tendermint-light-client-detector"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1ac1607eb7a3393313558b339c36eebeba15aa7f2d101d1d47299e65825152"
dependencies = [
"crossbeam-channel",
"derive_more 0.99.18",
"flex-error",
"futures",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"static_assertions",
"tendermint 0.38.1",
"tendermint-light-client",
"tendermint-proto 0.38.1",
"tendermint-rpc",
"time",
"tracing", "tracing",
] ]
@ -3458,6 +3879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02f96a2b8a0d3d0b59e4024b1a6bdc1589efc6af4709d08a480a20cc4ba90f63" checksum = "02f96a2b8a0d3d0b59e4024b1a6bdc1589efc6af4709d08a480a20cc4ba90f63"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"async-tungstenite",
"bytes", "bytes",
"flex-error", "flex-error",
"futures", "futures",
@ -3465,7 +3887,7 @@ dependencies = [
"peg", "peg",
"pin-project", "pin-project",
"rand", "rand",
"reqwest", "reqwest 0.11.27",
"semver", "semver",
"serde", "serde",
"serde_bytes", "serde_bytes",
@ -3504,6 +3926,16 @@ dependencies = [
"syn 2.0.76", "syn 2.0.76",
] ]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.36" version = "0.3.36"
@ -3550,6 +3982,26 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tm-prover"
version = "0.1.0"
dependencies = [
"clap",
"color-eyre",
"cosmrs",
"cw-proof",
"futures",
"serde",
"serde_json",
"tendermint 0.38.1",
"tendermint-light-client",
"tendermint-light-client-detector",
"tendermint-rpc",
"tokio",
"tracing",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tm-stateless-verifier" name = "tm-stateless-verifier"
version = "0.1.0" version = "0.1.0"
@ -3586,13 +4038,45 @@ dependencies = [
"syn 2.0.76", "syn 2.0.76",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.24.1" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [ dependencies = [
"rustls", "rustls 0.21.12",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls 0.23.12",
"rustls-pki-types",
"tokio", "tokio",
] ]
@ -3678,7 +4162,7 @@ dependencies = [
"socket2", "socket2",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tower", "tower 0.4.13",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing", "tracing",
@ -3717,6 +4201,16 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
dependencies = [
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@ -3760,6 +4254,21 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers",
"once_cell",
"regex",
"sharded-slab",
"thread_local",
"tracing",
"tracing-core",
]
[[package]] [[package]]
name = "transfers-contract" name = "transfers-contract"
version = "0.1.0" version = "0.1.0"
@ -3784,6 +4293,27 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tungstenite"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 1.1.0",
"httparse",
"log",
"rand",
"rustls 0.22.4",
"rustls-pki-types",
"sha1",
"thiserror",
"url",
"utf-8",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -3844,12 +4374,24 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.10.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -3948,6 +4490,18 @@ version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "wasmd-client"
version = "0.1.0"
dependencies = [
"anyhow",
"cosmrs",
"hex",
"reqwest 0.12.7",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.70" version = "0.3.70"
@ -3958,6 +4512,15 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "webpki-roots"
version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.9"
@ -3967,6 +4530,36 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "windows-registry"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
dependencies = [
"windows-result",
"windows-strings",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"

View file

@ -15,7 +15,9 @@ default = []
[dependencies] [dependencies]
# external # external
anyhow = { version = "1.0.86", default-features = false } async-trait = "0.1.81"
anyhow = { version = "1.0.86" }
base64 = "0.22.1"
clap = { version = "4.1.8", default-features = false, features = ["derive", "std"] } clap = { version = "4.1.8", default-features = false, features = ["derive", "std"] }
color-eyre = { version = "0.6.2", default-features = false } color-eyre = { version = "0.6.2", default-features = false }
ecies = { version = "0.2.3", default-features = false, features = ["pure"] } ecies = { version = "0.2.3", default-features = false, features = ["pure"] }
@ -25,19 +27,26 @@ prost = { version = "=0.13.1", default-features = false }
serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde = { version = "1.0.203", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] } serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] }
sha2 = { version = "0.10.8", default-features = false } sha2 = { version = "0.10.8", default-features = false }
reqwest = "0.12.7"
thiserror = { version = "1.0.49", default-features = false } thiserror = { version = "1.0.49", default-features = false }
tokio = { version = "=1.39.2", default-features = false, features = ["macros", "rt"] } tokio = { version = "=1.39.2", default-features = false, features = ["macros", "rt"] }
tonic = { version = "=0.12.1", default-features = false, features = ["codegen", "prost", "transport"] } tonic = { version = "=0.12.1", default-features = false, features = ["codegen", "prost", "transport"] }
tracing = "0.1.39"
futures-util = "0.3.30"
# cosmos # cosmos
cosmrs = { version = "=0.17.0", default-features = false } cosmrs = { version = "=0.17.0", default-features = false }
cosmwasm-std = { version = "2.1.1", default-features = false, features = ["std"] } cosmwasm-std = { version = "2.1.1", default-features = false, features = ["std"] }
tendermint = { version = "=0.38.1", default-features = false } tendermint = { version = "=0.38.1", default-features = false }
tendermint-rpc = { version = "=0.38.1", default-features = false }
tendermint-light-client = { version = "=0.38.1", default-features = false, features = ["rust-crypto"] } tendermint-light-client = { version = "=0.38.1", default-features = false, features = ["rust-crypto"] }
transfers-contract = { path = "../contracts", default-features = false } transfers-contract = { path = "../contracts", default-features = false }
# quartz # quartz
# quartz-common = { git = "ssh://git@github.com/informalsystems/cycles-quartz.git", features=["full"]}
quartz-common = { path = "../../../core/quartz-common", features=["full"]} quartz-common = { path = "../../../core/quartz-common", features=["full"]}
wasmd-client = { path = "../../../cosmwasm/packages/wasmd-client"}
tm-prover = { path = "../../../utils/tm-prover", default-features = false }
[dev-dependencies] [dev-dependencies]
cw-multi-test = "2.1.0" cw-multi-test = "2.1.0"

View file

@ -2,7 +2,8 @@ use std::{env, net::SocketAddr};
use clap::Parser; use clap::Parser;
use color_eyre::eyre::{eyre, Result}; use color_eyre::eyre::{eyre, Result};
use cosmrs::{tendermint::Hash, AccountId}; use cosmrs::AccountId;
use tendermint::Hash;
use tendermint_light_client::types::{Height, TrustThreshold}; use tendermint_light_client::types::{Height, TrustThreshold};
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> { fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
@ -53,10 +54,16 @@ pub struct Cli {
/// Maximum block lag, in seconds /// Maximum block lag, in seconds
#[clap(long, default_value = "5")] #[clap(long, default_value = "5")]
pub max_block_lag: u64, pub max_block_lag: u64,
#[clap(long, default_value = "127.0.0.1:11090")]
pub node_url: String,
#[clap(long, default_value = "admin")]
pub tx_sender: String,
} }
fn default_rpc_addr() -> SocketAddr { fn default_rpc_addr() -> SocketAddr {
let port = env::var("QUARTZ_PORT").unwrap_or_else(|_| "11090".to_string()); let port = env::var("QUARTZ_ENCLAVE_RPC_PORT").unwrap_or_else(|_| "11090".to_string());
format!("127.0.0.1:{}", port) format!("127.0.0.1:{}", port)
.parse() .parse()
.expect("Invalid socket address") .expect("Invalid socket address")

View file

@ -16,6 +16,7 @@ pub mod cli;
pub mod proto; pub mod proto;
pub mod state; pub mod state;
pub mod transfers_server; pub mod transfers_server;
pub mod wslistener;
use std::{ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -24,16 +25,13 @@ use std::{
use clap::Parser; use clap::Parser;
use cli::Cli; use cli::Cli;
use proto::settlement_server::SettlementServer as TransfersServer;
use quartz_common::{ use quartz_common::{
contract::state::{Config, LightClientOpts}, contract::state::{Config, LightClientOpts},
enclave::{ enclave::{
attestor::{Attestor, DefaultAttestor}, attestor::{Attestor, DefaultAttestor},
server::CoreService, server::{QuartzServer, WsListenerConfig},
}, },
proto::core_server::CoreServer,
}; };
use tonic::transport::Server;
use transfers_server::TransfersService; use transfers_server::TransfersService;
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
@ -41,7 +39,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Cli::parse(); let args = Cli::parse();
let light_client_opts = LightClientOpts::new( let light_client_opts = LightClientOpts::new(
args.chain_id, args.chain_id.clone(),
args.trusted_height.into(), args.trusted_height.into(),
Vec::from(args.trusted_hash) Vec::from(args.trusted_hash)
.try_into() .try_into()
@ -64,17 +62,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
args.tcbinfo_contract.map(|c| c.to_string()), args.tcbinfo_contract.map(|c| c.to_string()),
); );
let ws_config = WsListenerConfig {
node_url: args.node_url,
tx_sender: args.tx_sender,
trusted_hash: args.trusted_hash,
trusted_height: args.trusted_height,
chain_id: args.chain_id,
};
let sk = Arc::new(Mutex::new(None)); let sk = Arc::new(Mutex::new(None));
Server::builder() QuartzServer::new(config.clone(), sk.clone(), attestor.clone(), ws_config)
.add_service(CoreServer::new(CoreService::new( .add_service(TransfersService::new(config, sk, attestor))
config.clone(),
sk.clone(),
attestor.clone(),
)))
.add_service(TransfersServer::new(TransfersService::new(
config, sk, attestor,
)))
.serve(args.rpc_addr) .serve(args.rpc_addr)
.await?; .await?;

View file

@ -23,6 +23,12 @@ pub struct QueryResponse {
#[prost(string, tag = "1")] #[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String, pub message: ::prost::alloc::string::String,
} }
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
pub struct ListenRequest {}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
pub struct ListenResponse {}
/// Generated client implementations. /// Generated client implementations.
pub mod settlement_client { pub mod settlement_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
@ -151,6 +157,115 @@ pub mod settlement_client {
} }
} }
} }
/// Generated client implementations.
pub mod event_listener_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
use tonic::codegen::http::Uri;
#[derive(Debug, Clone)]
pub struct EventListenerClient<T> {
inner: tonic::client::Grpc<T>,
}
impl EventListenerClient<tonic::transport::Channel> {
/// Attempt to create a new client by connecting to a given endpoint.
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
where
D: TryInto<tonic::transport::Endpoint>,
D::Error: Into<StdError>,
{
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
Ok(Self::new(conn))
}
}
impl<T> EventListenerClient<T>
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,
{
pub fn new(inner: T) -> Self {
let inner = tonic::client::Grpc::new(inner);
Self { inner }
}
pub fn with_origin(inner: T, origin: Uri) -> Self {
let inner = tonic::client::Grpc::with_origin(inner, origin);
Self { inner }
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> EventListenerClient<InterceptedService<T, F>>
where
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
Response = http::Response<
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
>,
>,
<T as tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
>>::Error: Into<StdError> + Send + Sync,
{
EventListenerClient::new(InterceptedService::new(inner, interceptor))
}
/// Compress requests with the given encoding.
///
/// This requires the server to support it otherwise it might respond with an
/// error.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.send_compressed(encoding);
self
}
/// Enable decompressing responses.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.accept_compressed(encoding);
self
}
/// Limits the maximum size of a decoded message.
///
/// Default: `4MB`
#[must_use]
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
self.inner = self.inner.max_decoding_message_size(limit);
self
}
/// Limits the maximum size of an encoded message.
///
/// Default: `usize::MAX`
#[must_use]
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
self.inner = self.inner.max_encoding_message_size(limit);
self
}
pub async fn listen(
&mut self,
request: impl tonic::IntoRequest<super::ListenRequest>,
) -> std::result::Result<tonic::Response<super::ListenResponse>, tonic::Status> {
self.inner
.ready()
.await
.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/transfers.EventListener/Listen",
);
let mut req = request.into_request();
req.extensions_mut()
.insert(GrpcMethod::new("transfers.EventListener", "Listen"));
self.inner.unary(req, path, codec).await
}
}
}
/// Generated server implementations. /// Generated server implementations.
pub mod settlement_server { pub mod settlement_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
@ -363,3 +478,170 @@ pub mod settlement_server {
const NAME: &'static str = "transfers.Settlement"; const NAME: &'static str = "transfers.Settlement";
} }
} }
/// Generated server implementations.
pub mod event_listener_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
/// Generated trait containing gRPC methods that should be implemented for use with EventListenerServer.
#[async_trait]
pub trait EventListener: Send + Sync + 'static {
async fn listen(
&self,
request: tonic::Request<super::ListenRequest>,
) -> std::result::Result<tonic::Response<super::ListenResponse>, tonic::Status>;
}
#[derive(Debug)]
pub struct EventListenerServer<T: EventListener> {
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: EventListener> EventListenerServer<T> {
pub fn new(inner: T) -> Self {
Self::from_arc(Arc::new(inner))
}
pub fn from_arc(inner: Arc<T>) -> Self {
Self {
inner,
accept_compression_encodings: Default::default(),
send_compression_encodings: Default::default(),
max_decoding_message_size: None,
max_encoding_message_size: None,
}
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> InterceptedService<Self, F>
where
F: tonic::service::Interceptor,
{
InterceptedService::new(Self::new(inner), interceptor)
}
/// Enable decompressing requests with the given encoding.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.accept_compression_encodings.enable(encoding);
self
}
/// Compress responses with the given encoding, if the client supports it.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.send_compression_encodings.enable(encoding);
self
}
/// Limits the maximum size of a decoded message.
///
/// Default: `4MB`
#[must_use]
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
self.max_decoding_message_size = Some(limit);
self
}
/// Limits the maximum size of an encoded message.
///
/// Default: `usize::MAX`
#[must_use]
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
self.max_encoding_message_size = Some(limit);
self
}
}
impl<T, B> tonic::codegen::Service<http::Request<B>> for EventListenerServer<T>
where
T: EventListener,
B: Body + Send + 'static,
B::Error: Into<StdError> + Send + 'static,
{
type Response = http::Response<tonic::body::BoxBody>;
type Error = std::convert::Infallible;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(
&mut self,
_cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<B>) -> Self::Future {
match req.uri().path() {
"/transfers.EventListener/Listen" => {
#[allow(non_camel_case_types)]
struct ListenSvc<T: EventListener>(pub Arc<T>);
impl<
T: EventListener,
> tonic::server::UnaryService<super::ListenRequest>
for ListenSvc<T> {
type Response = super::ListenResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<super::ListenRequest>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as EventListener>::listen(&inner, request).await
};
Box::pin(fut)
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let max_decoding_message_size = self.max_decoding_message_size;
let max_encoding_message_size = self.max_encoding_message_size;
let inner = self.inner.clone();
let fut = async move {
let method = ListenSvc(inner);
let codec = tonic::codec::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
send_compression_encodings,
)
.apply_max_message_size_config(
max_decoding_message_size,
max_encoding_message_size,
);
let res = grpc.unary(method, req).await;
Ok(res)
};
Box::pin(fut)
}
_ => {
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(),
)
})
}
}
}
}
impl<T: EventListener> Clone for EventListenerServer<T> {
fn clone(&self) -> Self {
let inner = self.inner.clone();
Self {
inner,
accept_compression_encodings: self.accept_compression_encodings,
send_compression_encodings: self.send_compression_encodings,
max_decoding_message_size: self.max_decoding_message_size,
max_encoding_message_size: self.max_encoding_message_size,
}
}
}
impl<T: EventListener> tonic::server::NamedService for EventListenerServer<T> {
const NAME: &'static str = "transfers.EventListener";
}
}

View file

@ -11,7 +11,10 @@ use quartz_common::{
msg::execute::attested::{HasUserData, RawAttested}, msg::execute::attested::{HasUserData, RawAttested},
state::{Config, UserData}, state::{Config, UserData},
}, },
enclave::{attestor::Attestor, server::ProofOfPublication}, enclave::{
attestor::Attestor,
server::{IntoServer, ProofOfPublication},
},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@ -20,11 +23,20 @@ use transfers_contract::msg::execute::{ClearTextTransferRequestMsg, Request as T
use crate::{ use crate::{
proto::{ proto::{
settlement_server::Settlement, QueryRequest, QueryResponse, UpdateRequest, UpdateResponse, settlement_server::{Settlement, SettlementServer},
QueryRequest, QueryResponse, UpdateRequest, UpdateResponse,
}, },
state::{RawBalance, RawState, State}, state::{RawBalance, RawState, State},
}; };
impl<A: Attestor> IntoServer for TransfersService<A> {
type Server = SettlementServer<TransfersService<A>>;
fn into_server(self) -> Self::Server {
SettlementServer::new(self)
}
}
pub type RawCipherText = HexBinary; pub type RawCipherText = HexBinary;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -34,17 +46,17 @@ pub struct TransfersService<A> {
attestor: A, attestor: A,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct UpdateRequestMessage { pub struct UpdateRequestMessage {
state: HexBinary, pub state: HexBinary,
requests: Vec<TransfersRequest>, pub requests: Vec<TransfersRequest>,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct QueryRequestMessage { pub struct QueryRequestMessage {
state: HexBinary, pub state: HexBinary,
address: Addr, pub address: Addr,
ephemeral_pubkey: HexBinary, pub ephemeral_pubkey: HexBinary,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]

View file

@ -0,0 +1,321 @@
//TODO: get rid of this
use std::str::FromStr;
use anyhow::{anyhow, Result};
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};
use cosmwasm_std::{Addr, HexBinary};
use futures_util::StreamExt;
use quartz_common::{
contract::msg::execute::attested::{
MockAttestation, RawAttested, RawAttestedMsgSansHandler, RawMockAttestation,
},
enclave::{
attestor::Attestor,
server::{WebSocketHandler, WsListenerConfig},
},
};
use reqwest::Url;
use serde_json::json;
use tendermint_rpc::{event::Event, query::EventType, SubscriptionClient, WebSocketClient};
use tm_prover::{config::Config as TmProverConfig, prover::prove};
use tonic::Request;
use tracing::info;
use transfers_contract::msg::{
execute::{QueryResponseMsg, Request as TransferRequest, UpdateMsg},
AttestedMsg, ExecuteMsg,
QueryMsg::{GetRequests, GetState},
};
use wasmd_client::{CliWasmdClient, QueryResult, WasmdClient};
use crate::{
proto::{settlement_server::Settlement, QueryRequest, UpdateRequest},
transfers_server::{QueryRequestMessage, TransfersService, UpdateRequestMessage},
};
// TODO: Need to prevent listener from taking actions until handshake is completed
#[async_trait::async_trait]
impl<A: Attestor> WebSocketHandler for TransfersService<A> {
async fn handle(&self, event: Event, config: WsListenerConfig) -> Result<()> {
// Validation
let is_transfer = is_transfer_event(&event);
let is_query = is_query_event(&event);
if !is_transfer && !is_query {
return Ok(());
} else {
let mut sender = None;
let mut contract_address = None;
let mut emphemeral_pubkey = None;
if let Some(events) = &event.events {
for (key, values) in events {
match key.as_str() {
"message.sender" => {
sender = values.first().cloned();
}
"execute._contract_address" => {
contract_address = values.first().cloned();
}
"wasm-query_balance.emphemeral_pubkey" => {
// TODO: fix typo
emphemeral_pubkey = values.first().cloned();
}
_ => {}
}
}
}
if contract_address.is_none() {
return Ok(());
}
if is_transfer {
println!("Processing transfer event");
transfer_handler(
self,
&contract_address
.expect("must be included in transfers event")
.parse::<AccountId>()
.map_err(|e| anyhow!(e))?,
&config,
)
.await?;
} else if is_query {
println!("Processing query event");
query_handler(
self,
&contract_address
.expect("must be included in query event")
.parse::<AccountId>()
.map_err(|e| anyhow!(e))?,
sender.expect("must be included in query event"),
emphemeral_pubkey.expect("must be included in query event"),
&config,
)
.await?;
}
}
Ok(())
}
}
fn is_transfer_event(event: &Event) -> bool {
// Check if the event is a transaction type
if let Some(EventType::Tx) = event.event_type() {
// Check for the "wasm.action" key with the value "init_clearing"
if let Some(events) = &event.events {
return events.iter().any(|(key, _)| key == "wasm-transfer.action");
}
}
false
}
fn is_query_event(event: &Event) -> bool {
// Check if the event is a transaction type
if let Some(EventType::Tx) = event.event_type() {
// Check for the "wasm.action" key with the value "init_clearing"
if let Some(events) = &event.events {
return events
.iter()
.any(|(key, _)| key.starts_with("wasm-query_balance"));
}
}
false
}
async fn transfer_handler<A: Attestor>(
client: &TransfersService<A>,
contract: &AccountId,
ws_config: &WsListenerConfig,
) -> Result<()> {
let chain_id = &ChainId::from_str(&ws_config.chain_id)?;
let httpurl = Url::parse(&format!("http://{}", ws_config.node_url))?;
let wasmd_client = CliWasmdClient::new(httpurl.clone());
// Query chain
// Get epoch, obligations, liquidity sources
let resp: QueryResult<Vec<TransferRequest>> = wasmd_client
.query_smart(contract, json!(GetRequests {}))
.map_err(|e| anyhow!("Problem querying epoch: {}", e))?;
let requests = resp.data;
let resp: QueryResult<HexBinary> = wasmd_client
.query_smart(contract, json!(GetState {}))
.map_err(|e| anyhow!("Problem querying epoch: {}", 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?;
// Call tm prover with trusted hash and height
let prover_config = TmProverConfig {
primary: httpurl.as_str().parse()?,
witnesses: httpurl.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
contract_address: contract.clone(),
storage_key: "requests".to_string(),
chain_id: ws_config.chain_id.to_string(),
..Default::default()
};
let proof_output = tokio::task::spawn_blocking(move || {
// Create a new runtime inside the blocking thread.
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
prove(prover_config)
.await
.map_err(|report| anyhow!("Tendermint prover failed. Report: {}", report))
})
})
.await??; // Handle both JoinError and your custom error
// Merge the UpdateRequestMessage with the proof
let mut proof_json = serde_json::to_value(proof_output)?;
proof_json["msg"] = serde_json::to_value(&update_contents)?;
// Build final request object
let request = Request::new(UpdateRequest {
message: json!(proof_json).to_string(),
});
// Send UpdateRequestMessage request to enclave over tonic gRPC client
let update_response = client
.run(request)
.await
.map_err(|e| anyhow!("Failed to communicate to relayer. {e}"))?
.into_inner();
// Extract json from enclave response
let attested: RawAttested<UpdateMsg, HexBinary> =
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 setoffs_msg = ExecuteMsg::Update::<RawMockAttestation>(AttestedMsg {
msg: RawAttestedMsgSansHandler(attested.msg),
attestation: MockAttestation(
attested
.attestation
.as_slice()
.try_into()
.map_err(|_| anyhow!("slice with incorrect length"))?,
)
.into(),
});
// Post response to chain
let output = wasmd_client.tx_execute(
contract,
chain_id,
2000000,
&ws_config.tx_sender,
json!(setoffs_msg),
)?;
println!("Output TX: {}", output);
Ok(())
}
async fn query_handler<A: Attestor>(
client: &TransfersService<A>,
contract: &AccountId,
msg_sender: String,
pubkey: String,
ws_config: &WsListenerConfig,
) -> Result<()> {
let chain_id = &ChainId::from_str(&ws_config.chain_id)?;
let httpurl = Url::parse(&format!("http://{}", ws_config.node_url))?;
let wasmd_client = CliWasmdClient::new(httpurl);
// Query Chain
// Get state
let resp: QueryResult<HexBinary> = wasmd_client
.query_smart(contract, json!(GetState {}))
.map_err(|e| anyhow!("Problem querying epoch: {}", e))?;
let state = resp.data;
// Build request
let update_contents = QueryRequestMessage {
state,
address: Addr::unchecked(&msg_sender), // sender comes from TX event, therefore is checked
ephemeral_pubkey: HexBinary::from_hex(&pubkey)?,
};
// Send QueryRequestMessage to enclave over tonic gRPC client
let request = Request::new(QueryRequest {
message: json!(update_contents).to_string(),
});
let query_response = client
.query(request)
.await
.map_err(|e| anyhow!("Failed to communicate to relayer. {e}"))?
.into_inner();
// Extract json from the enclave response
let attested: RawAttested<QueryResponseMsg, HexBinary> =
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 setoffs_msg = ExecuteMsg::QueryResponse::<RawMockAttestation>(AttestedMsg {
msg: RawAttestedMsgSansHandler(attested.msg),
attestation: MockAttestation(
attested
.attestation
.as_slice()
.try_into()
.map_err(|_| anyhow!("slice with incorrect length"))?,
)
.into(),
});
// Post response to chain
let output = wasmd_client.tx_execute(
contract,
chain_id,
2000000,
&ws_config.tx_sender,
json!(setoffs_msg),
)?;
println!("Output TX: {}", output);
Ok(())
}
async fn two_block_waitoor(wsurl: &str) -> Result<(), anyhow::Error> {
let (client, driver) = WebSocketClient::new(wsurl).await?;
let driver_handle = tokio::spawn(async move { driver.run().await });
// Subscription functionality
let mut subs = client.subscribe(EventType::NewBlock.into()).await?;
// Wait 2 NewBlock events
let mut ev_count = 2_i32;
while let Some(res) = subs.next().await {
let _ev = res?;
ev_count -= 1;
if ev_count == 0 {
break;
}
}
// Signal to the driver to terminate.
client.close()?;
// Await the driver's termination to ensure proper connection closure.
let _ = driver_handle.await?;
Ok(())
}

View file

@ -52,8 +52,7 @@ tendermint-light-client.workspace = true
tendermint-rpc = { workspace = true, features=["websocket-client", "http-client"]} tendermint-rpc = { workspace = true, features=["websocket-client", "http-client"]}
tm-prover = { workspace = true} tm-prover = { workspace = true}
quartz-common = { workspace = true, features=["contract"]} quartz-common = { workspace = true, features=["contract", "proto"]}
quartz-tee-ra = { workspace = true} quartz-tee-ra = { workspace = true}
mtcs-enclave = { workspace = true, optional = false}
wasmd-client.workspace = true wasmd-client.workspace = true
tempfile.workspace = true tempfile.workspace = true

View file

@ -38,6 +38,10 @@ impl Handler for EnclaveStartRequest {
trusted_height.to_string(), trusted_height.to_string(),
"--trusted-hash".to_string(), "--trusted-hash".to_string(),
trusted_hash.to_string(), trusted_hash.to_string(),
"--node-url".to_string(),
config.node_url,
"--tx-sender".to_string(),
config.tx_sender,
]; ];
// Run quartz enclave and block // Run quartz enclave and block
@ -98,7 +102,7 @@ async fn create_mock_enclave_child(
// Use the enclave package metadata to get the path to the program binary // Use the enclave package metadata to get the path to the program binary
let package_name = MetadataCommand::new() let package_name = MetadataCommand::new()
.manifest_path(&enclave_dir.join("Cargo.toml")) .manifest_path(enclave_dir.join("Cargo.toml"))
.exec() .exec()
.map_err(|e| Error::GenericErr(e.to_string()))? .map_err(|e| Error::GenericErr(e.to_string()))?
.root_package() .root_package()

View file

@ -1,4 +1,4 @@
use std::{fs, str::FromStr}; use std::str::FromStr;
use anyhow::anyhow; use anyhow::anyhow;
use async_trait::async_trait; use async_trait::async_trait;
@ -57,7 +57,7 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
info!("Running SessionCreate"); info!("Running SessionCreate");
let res: serde_json::Value = RelayMessage::SessionCreate let res = RelayMessage::SessionCreate
.run_relay(config.enclave_rpc(), config.mock_sgx) .run_relay(config.enclave_rpc(), config.mock_sgx)
.await?; .await?;
@ -82,33 +82,26 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String, any
info!("Waiting 2 blocks for light client proof"); info!("Waiting 2 blocks for light client proof");
two_block_waitoor(&wsurl).await?; two_block_waitoor(&wsurl).await?;
let proof_path = config.cache_dir()?.join("light-client-proof.json");
debug!("Proof path: {:?}", proof_path.to_str());
// Call tm prover with trusted hash and height // Call tm prover with trusted hash and height
let prover_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,
trusted_hash, trusted_hash,
trace_file: Some(proof_path.clone()),
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: config.chain_id.to_string(), chain_id: config.chain_id.to_string(),
..Default::default() ..Default::default()
}; };
debug!("config: {:?}", prover_config);
if let Err(report) = prove(prover_config).await {
return Err(anyhow!("Tendermint prover failed. Report: {}", report));
}
// Read proof file let proof_output = prove(prover_config)
let proof = fs::read_to_string(proof_path.as_path())?; .await
.map_err(|report| anyhow!("Tendermint prover failed. Report: {}", report))?;
// Execute SessionSetPubKey on enclave // Execute SessionSetPubKey on enclave
info!("Running SessionSetPubKey"); info!("Running SessionSetPubKey");
let res: serde_json::Value = RelayMessage::SessionSetPubKey(proof.trim().to_string()) let res = RelayMessage::SessionSetPubKey(serde_json::to_string(&proof_output)?)
.run_relay(config.enclave_rpc(), config.mock_sgx) .run_relay(config.enclave_rpc(), config.mock_sgx)
.await?; .await?;

View file

@ -16,23 +16,29 @@ mock-sgx = ["quartz-cw/mock-sgx"]
[dependencies] [dependencies]
# external # external
anyhow.workspace = true
async-trait.workspace = true
sha2 = { workspace = true } sha2 = { workspace = true }
clap.workspace = true clap.workspace = true
color-eyre.workspace = true color-eyre.workspace = true
ecies.workspace = true ecies.workspace = true
futures-util.workspace = true
hex.workspace = true hex.workspace = true
k256.workspace = true k256.workspace = true
rand.workspace = true rand.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
thiserror.workspace = true
tonic.workspace = true tonic.workspace = true
tokio.workspace = true tokio.workspace = true
tower.workspace = true
# cosmos # cosmos
cosmrs.workspace = true cosmrs.workspace = true
cosmwasm-std.workspace = true cosmwasm-std.workspace = true
tendermint.workspace = true tendermint.workspace = true
tendermint-light-client.workspace = true tendermint-light-client.workspace = true
tendermint-rpc = { workspace = true, features=["websocket-client", "http-client"] }
# quartz # quartz
cw-proof.workspace = true cw-proof.workspace = true

View file

@ -15,7 +15,7 @@ pub type DefaultAttestor = EpidAttestor;
pub type DefaultAttestor = MockAttestor; pub type DefaultAttestor = MockAttestor;
/// The trait defines the interface for generating attestations from within an enclave. /// The trait defines the interface for generating attestations from within an enclave.
pub trait Attestor { pub trait Attestor: Send + Sync + 'static {
type Error: ToString; type Error: ToString;
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error>; fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error>;

15
core/quartz/src/error.rs Normal file
View file

@ -0,0 +1,15 @@
use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum QuartzError {
#[error("WebSocket error: {0}")]
WebSocket(#[from] tendermint_rpc::Error),
#[error("Tonic transport error: {0}")]
TonicTransport(#[from] tonic::transport::Error),
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("Other error: {0}")]
Other(String),
}

View file

@ -13,5 +13,6 @@
)] )]
pub mod attestor; pub mod attestor;
pub mod error;
pub mod server; pub mod server;
pub mod types; pub mod types;

View file

@ -1,4 +1,6 @@
use std::{ use std::{
convert::Infallible,
net::SocketAddr,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
time::Duration, time::Duration,
}; };
@ -7,6 +9,7 @@ use cw_proof::proof::{
cw::{CwProof, RawCwProof}, cw::{CwProof, RawCwProof},
Proof, Proof,
}; };
use futures_util::StreamExt;
use k256::ecdsa::SigningKey; use k256::ecdsa::SigningKey;
use quartz_cw::{ use quartz_cw::{
msg::{ msg::{
@ -16,26 +19,156 @@ use quartz_cw::{
state::{Config, LightClientOpts, Nonce, Session}, state::{Config, LightClientOpts, Nonce, Session},
}; };
use quartz_proto::quartz::{ use quartz_proto::quartz::{
core_server::Core, InstantiateRequest as RawInstantiateRequest, core_server::{Core, CoreServer},
InstantiateResponse as RawInstantiateResponse, SessionCreateRequest as RawSessionCreateRequest, InstantiateRequest as RawInstantiateRequest, InstantiateResponse as RawInstantiateResponse,
SessionCreateRequest as RawSessionCreateRequest,
SessionCreateResponse as RawSessionCreateResponse, SessionCreateResponse as RawSessionCreateResponse,
SessionSetPubKeyRequest as RawSessionSetPubKeyRequest, SessionSetPubKeyRequest as RawSessionSetPubKeyRequest,
SessionSetPubKeyResponse as RawSessionSetPubKeyResponse, SessionSetPubKeyResponse as RawSessionSetPubKeyResponse,
}; };
use rand::Rng; use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tendermint::{block::Height, Hash};
use tendermint_light_client::{ use tendermint_light_client::{
light_client::Options, light_client::Options,
types::{LightBlock, TrustThreshold}, types::{LightBlock, TrustThreshold},
}; };
use tendermint_rpc::{
event::Event,
query::{EventType, Query},
SubscriptionClient, WebSocketClient,
};
use tm_stateless_verifier::make_provider; use tm_stateless_verifier::make_provider;
use tonic::{Request, Response, Result as TonicResult, Status}; use tonic::{
body::BoxBody,
codegen::http,
server::NamedService,
transport::{server::Router, Server},
Request, Response, Result as TonicResult, Status,
};
use tower::Service;
use crate::{ use crate::{
attestor::Attestor, attestor::{Attestor, DefaultAttestor},
error::QuartzError,
types::{InstantiateResponse, SessionCreateResponse, SessionSetPubKeyResponse}, types::{InstantiateResponse, SessionCreateResponse, SessionSetPubKeyResponse},
}; };
/// Trait for Quartz enclaves to process on-chain events.
///
/// Implementors of this trait should define how to process incoming WebSocket events,
/// using the provided `event` and `ws_config` parameters.
///
/// # Arguments
///
/// * `event` - The WebSocket event received from the Tendermint RPC server.
/// * `ws_config` - Configuration values used for handling the WebSocket events,
/// such as node URL, a signer for transactions, and trusted block information.
///
/// # Returns
///
/// An `anyhow::Result<()>` indicating success or failure in handling the event.
#[tonic::async_trait]
pub trait WebSocketHandler: Send + Sync + 'static {
async fn handle(&self, event: Event, ws_config: WsListenerConfig) -> anyhow::Result<()>; // TODO: replace anyhow
}
#[derive(Debug, Clone)]
pub struct WsListenerConfig {
pub node_url: String,
pub chain_id: String,
pub tx_sender: String,
pub trusted_hash: Hash,
pub trusted_height: Height,
}
/// A trait for wrapping a tonic service with the gRPC server handler
pub trait IntoServer {
type Server;
fn into_server(self) -> Self::Server;
}
pub struct QuartzServer {
pub router: Router,
ws_handlers: Vec<Box<dyn WebSocketHandler>>,
pub ws_config: WsListenerConfig,
}
impl QuartzServer {
pub fn new(
config: Config,
sk: Arc<Mutex<Option<SigningKey>>>,
attestor: DefaultAttestor,
ws_config: WsListenerConfig,
) -> Self {
let core_service = CoreServer::new(CoreService::new(config, sk.clone(), attestor.clone()));
Self {
router: Server::builder().add_service(core_service),
ws_handlers: Vec::new(),
ws_config,
}
}
pub fn add_service<S>(mut self, service: S) -> Self
where
S: IntoServer + WebSocketHandler + Clone,
S::Server: Service<
http::request::Request<BoxBody>,
Response = http::response::Response<BoxBody>,
Error = Infallible,
> + NamedService
+ Clone
+ Send
+ 'static,
<S::Server as Service<http::request::Request<BoxBody>>>::Future: Send + 'static,
{
self.ws_handlers.push(Box::new(service.clone()));
let tonic_server = service.into_server();
self.router = self.router.add_service(tonic_server);
self
}
pub async fn serve(self, addr: SocketAddr) -> Result<(), QuartzError> {
// Launch all WebSocket handlers as separate Tokio tasks
tokio::spawn(async move {
if let Err(e) = Self::websocket_events_listener(&self.ws_handlers, self.ws_config).await
{
eprintln!("Error in WebSocket event handler: {:?}", e);
}
});
Ok(self.router.serve(addr).await?)
}
async fn websocket_events_listener(
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 driver_handle = tokio::spawn(async move { driver.run().await });
let mut subs = client.subscribe(Query::from(EventType::Tx)).await.unwrap();
while let Some(Ok(event)) = subs.next().await {
for handler in ws_handlers {
if let Err(e) = handler.handle(event.clone(), ws_config.clone()).await {
eprintln!("Error in event handler: {:?}", e);
}
}
}
// Close connection
client.close()?;
let _ = driver_handle.await;
Ok(())
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CoreService<A> { pub struct CoreService<A> {
config: Config, config: Config,

View file

@ -12,9 +12,9 @@ authors.workspace = true
path="src/lib.rs" path="src/lib.rs"
[dependencies] [dependencies]
anyhow.workspace = true
cosmrs.workspace = true cosmrs.workspace = true
hex.workspace = true hex.workspace = true
reqwest.workspace = true reqwest.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
anyhow.workspace = true

View file

@ -1,4 +1,4 @@
use std::{num::ParseIntError, path::PathBuf, str::FromStr}; use std::{num::ParseIntError, str::FromStr};
use clap::Parser; use clap::Parser;
use color_eyre::eyre::{eyre, Result}; use color_eyre::eyre::{eyre, Result};
@ -82,7 +82,6 @@ impl Default for Config {
trusting_period: 1209600u64, trusting_period: 1209600u64,
max_clock_drift: 5u64, max_clock_drift: 5u64,
max_block_lag: 5u64, max_block_lag: 5u64,
trace_file: None,
verbose: Verbosity::default(), verbose: Verbosity::default(),
contract_address: "wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70" contract_address: "wasm14qdftsfk6fwn40l0xmruga08xlczl4g05npy70"
.parse() .parse()
@ -131,10 +130,6 @@ pub struct Config {
#[clap(long, default_value = "5")] #[clap(long, default_value = "5")]
pub max_block_lag: u64, pub max_block_lag: u64,
/// Output file to store light client proof (AKA verification trace)
#[clap(long)]
pub trace_file: Option<PathBuf>,
/// Increase verbosity /// Increase verbosity
#[clap(flatten)] #[clap(flatten)]
pub verbose: Verbosity, pub verbose: Verbosity,

View file

@ -19,5 +19,8 @@ async fn main() -> Result<()> {
.finish() .finish()
.init(); .init();
prove(args).await let proof = prove(args).await?;
println!("{:?}", proof);
Ok(())
} }

View file

@ -1,5 +1,5 @@
#![deny( #![deny(
warnings, // warnings,
trivial_casts, trivial_casts,
trivial_numeric_casts, trivial_numeric_casts,
unused_import_braces, unused_import_braces,
@ -7,12 +7,7 @@
)] )]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
use std::{ use std::time::Duration;
fs::File,
io::{BufWriter, Write},
path::PathBuf,
time::Duration,
};
use color_eyre::{ use color_eyre::{
eyre::{eyre, Result}, eyre::{eyre, Result},
@ -49,12 +44,11 @@ pub async fn prove(
trusting_period, trusting_period,
max_clock_drift, max_clock_drift,
max_block_lag, max_block_lag,
trace_file,
verbose: _, verbose: _,
contract_address, contract_address,
storage_key, storage_key,
}: TmProverConfig, }: TmProverConfig,
) -> Result<()> { ) -> Result<ProofOutput> {
let options = Options { let options = Options {
trust_threshold, trust_threshold,
trusting_period: Duration::from_secs(trusting_period), trusting_period: Duration::from_secs(trusting_period),
@ -134,9 +128,8 @@ pub async fn prove(
.verify(latest_app_hash.clone().into()) .verify(latest_app_hash.clone().into())
.map_err(|e: ProofError| eyre!(e))?; .map_err(|e: ProofError| eyre!(e))?;
if let Some(trace_file) = trace_file { // Replace the last block in the trace (i.e., the (latest - 1) block) with the latest block
// replace the last block in the trace (i.e. the (latest - 1) block) with the latest block // We don't actually verify the latest block because it will be verified on the other side
// we don't actually verify the latest block because it will be verified on the other side
let latest_block = provider.fetch_light_block(latest_height)?; let latest_block = provider.fetch_light_block(latest_height)?;
let _ = primary_trace.pop(); let _ = primary_trace.pop();
primary_trace.push(latest_block); primary_trace.push(latest_block);
@ -145,20 +138,8 @@ pub async fn prove(
light_client_proof: primary_trace, light_client_proof: primary_trace,
merkle_proof: proof.into(), merkle_proof: proof.into(),
}; };
write_proof_to_file(trace_file, output).await?;
};
Ok(()) Ok(output)
}
async fn write_proof_to_file(trace_file: PathBuf, output: ProofOutput) -> Result<()> {
info!("Writing proof to output file ({})", trace_file.display());
let file = File::create(trace_file)?;
let mut writer = BufWriter::new(file);
serde_json::to_writer(&mut writer, &output)?;
writer.flush()?;
Ok(())
} }
async fn run_detector( async fn run_detector(