feat: cli enclave start (#156)

This commit is contained in:
Daniel Gushchyan 2024-08-09 16:24:57 -07:00 committed by GitHub
parent 048f8e7d0f
commit 4fd86c089a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 364 additions and 43 deletions

50
Cargo.lock generated
View file

@ -751,9 +751,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.13"
version = "4.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
dependencies = [
"clap_builder",
"clap_derive",
@ -761,9 +761,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.13"
version = "4.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
dependencies = [
"anstream",
"anstyle",
@ -914,15 +914,15 @@ dependencies = [
[[package]]
name = "cosmwasm-core"
version = "2.1.1"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "367fc87c43759098a476ef90f915aadc66c300480ad9c155b512081fbf327bc1"
checksum = "d905990ef3afb5753bb709dc7de88e9e370aa32bcc2f31731d4b533b63e82490"
[[package]]
name = "cosmwasm-crypto"
version = "2.1.1"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7c41f3e371ea457d3b98bb592c38858b46efcf614e0e988ec2ebbdb973954f"
checksum = "5b2a7bd9c1dd9a377a4dc0f4ad97d24b03c33798cd5a6d7ceb8869b41c5d2f2d"
dependencies = [
"ark-bls12-381",
"ark-ec",
@ -943,9 +943,9 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "2.1.1"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c10510e8eb66cf7e109741b1e2c76ad18f30b5a1daa064f5f7115c1f733aaea0"
checksum = "029910b409398fdf81955d7301b906caf81f2c42b013ea074fbd89720229c424"
dependencies = [
"proc-macro2",
"quote",
@ -954,9 +954,9 @@ dependencies = [
[[package]]
name = "cosmwasm-schema"
version = "2.1.1"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79879b6b7ef6a331b05030ce91ce46a7c4b0baf1ed6b382cce2e9a168109380"
checksum = "4bc0d4d85e83438ab9a0fea9348446f7268bc016aacfebce37e998559f151294"
dependencies = [
"cosmwasm-schema-derive",
"schemars",
@ -967,9 +967,9 @@ dependencies = [
[[package]]
name = "cosmwasm-schema-derive"
version = "2.1.1"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b53e33c0e97170c7ac9cb440f4bc599a07f9cbb9b7e87916cca37b1239d57b"
checksum = "edf5c8adac41bb7751c050d7c4c18675be19ee128714454454575e894424eeef"
dependencies = [
"proc-macro2",
"quote",
@ -978,9 +978,9 @@ dependencies = [
[[package]]
name = "cosmwasm-std"
version = "2.1.1"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92011c39570876f340d5f9defa68bf92797b1c44421f1b9ea9b04a31d6defd33"
checksum = "51dec99a2e478715c0a4277f0dbeadbb8466500eb7dec873d0924edd086e77f1"
dependencies = [
"base64 0.22.1",
"bech32",
@ -3627,6 +3627,7 @@ dependencies = [
"cosmrs",
"cosmwasm-std",
"cycles-sync",
"dirs",
"displaydoc",
"futures-util",
"hex",
@ -3641,6 +3642,7 @@ dependencies = [
"serde",
"serde_json",
"subtle-encoding",
"target-lexicon",
"tendermint 0.38.1",
"tendermint-light-client",
"tendermint-rpc",
@ -4597,6 +4599,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "signature"
version = "2.2.0"
@ -4764,6 +4775,12 @@ dependencies = [
"libc",
]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tempfile"
version = "3.10.1"
@ -5132,6 +5149,7 @@ dependencies = [
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",

View file

@ -0,0 +1,58 @@
# Quartz manifest file
loader.entrypoint = "file:{{ gramine.libos }}"
libos.entrypoint = "{{ quartz_dir }}/target/release/enclave"
loader.log_level = "{{ log_level }}"
loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"
loader.env.HOME = "{{ home }}"
loader.env.INSIDE_SGX = "1"
loader.env.TLS = { passthrough = true }
loader.env.RA_TYPE = { passthrough = true }
loader.env.RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE = { passthrough = true }
loader.env.RA_TLS_ALLOW_OUTDATED_TCB_INSECURE = { passthrough = true }
loader.env.RA_TLS_MRENCLAVE = { passthrough = true }
loader.env.RA_TLS_MRSIGNER = { passthrough = true }
loader.env.RA_TLS_ISV_SVN = { passthrough = true }
loader.env.RA_TLS_ISV_PROD_ID = { passthrough = true }
loader.env.RA_TLS_EPID_API_KEY = { passthrough = true }
loader.env.MYAPP_DATA = { passthrough = true }
loader.env.QUARTZ_PORT = { passthrough = true }
loader.argv = ["enclave",
"--chain-id", "testing",
"--trusted-height", "{{ trusted_height }}",
"--trusted-hash", "{{ trusted_hash }}"]
fs.mounts = [
{ uri = "file:{{ gramine.runtimedir() }}", path = "/lib" },
{ uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" },
{ uri = "file:/usr/{{ arch_libdir }}", path = "/usr{{ arch_libdir }}" },
{ uri = "file:{{ quartz_dir }}", path = "{{ quartz_dir }}" },
]
# sgx.debug = true
sgx.enclave_size = "512M"
sgx.max_threads = 4
sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
sgx.remote_attestation = "{{ ra_type }}"
sgx.ra_client_spid = "{{ ra_client_spid }}"
sgx.ra_client_linkable = {{ 'true' if ra_client_linkable == '1' else 'false' }}
sgx.trusted_files = [
"file:{{ gramine.libos }}",
"file:{{ quartz_dir }}/target/release/enclave",
"file:{{ gramine.runtimedir() }}/",
"file:{{ arch_libdir }}/",
"file:/usr/{{ arch_libdir }}/",
]
sgx.allowed_files = [
"file:{{ quartz_dir }}/exchange.sk",
"file:{{ quartz_dir }}/request.json",
]
sys.insecure__allow_eventfd = true
sys.enable_sigterm_injection = true

View file

@ -15,6 +15,7 @@ cargo-generate.workspace = true
clap = { workspace = true, features=["env"] }
color-eyre.workspace = true
displaydoc.workspace = true
dirs = "5.0.1"
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
@ -23,7 +24,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] }
hex.workspace = true
k256.workspace = true
prost.workspace = true
tokio.workspace = true
tokio = { workspace = true, features = ["process"] }
tonic.workspace = true
once_cell = "1.19.0"
reqwest = { version = "0.12.2", default-features = false, features = ["json", "rustls-tls"] }
@ -31,6 +32,7 @@ anyhow = "1.0.86"
base64 = "0.22.1"
subtle-encoding = "0.5.1"
futures-util = "0.3.30"
target-lexicon = "0.12.16"
# cosmos
cosmrs.workspace = true

View file

@ -117,14 +117,19 @@ pub enum ContractCommand {
pub enum EnclaveCommand {
/// Build the Quartz app's enclave
Build {
/// path to Cargo.toml file of the Quartz app's enclave package, defaults to './enclave/Cargo.toml' if unspecified
/// Path to Cargo.toml file of the Quartz app's enclave package, defaults to './enclave/Cargo.toml' if unspecified
#[arg(long, default_value = "./enclave/Cargo.toml")]
manifest_path: PathBuf,
},
// Run the Quartz app's enclave
Start {
/// Path to quartz app directory
/// Defaults to current working dir
#[clap(long)]
path: Option<PathBuf>,
app_dir: Option<PathBuf>,
/// The network chain ID
#[clap(long)]
chain_id: String,
},
}

View file

@ -7,6 +7,7 @@ pub mod utils;
pub mod contract_build;
pub mod contract_deploy;
pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
pub mod init;
@ -30,6 +31,7 @@ impl Handler for Request {
Request::ContractBuild(request) => request.handle(config).await,
Request::ContractDeploy(request) => request.handle(config).await,
Request::EnclaveBuild(request) => request.handle(config).await,
Request::EnclaveStart(request) => request.handle(config).await,
}
.map(Into::into)
}

View file

@ -0,0 +1,187 @@
use std::env;
use async_trait::async_trait;
use tokio::process::Command;
use tracing::{debug, info};
use super::utils::helpers::read_hash_height;
use crate::{
error::Error,
handler::Handler,
request::enclave_start::EnclaveStartRequest,
response::{enclave_start::EnclaveStartResponse, Response},
Config,
};
#[async_trait]
impl Handler for EnclaveStartRequest {
type Error = Error;
type Response = Response;
async fn handle(self, config: Config) -> Result<Self::Response, Self::Error> {
let enclave_dir = self.app_dir.join("enclave");
let (trusted_height, trusted_hash) = read_hash_height(self.app_dir.as_path())
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if config.mock_sgx {
let enclave_args: Vec<String> = vec![
"--chain-id".to_string(),
self.chain_id,
"--trusted-height".to_string(),
trusted_height.to_string(),
"--trusted-hash".to_string(),
trusted_hash.to_string(),
];
// Run quartz enclave and block
let _res = run_enclave(
enclave_dir.join("Cargo.toml").display().to_string(),
config.mock_sgx,
enclave_args,
)
.await?;
} else {
// set cwd to enclave app
env::set_current_dir(enclave_dir).map_err(|e| Error::GenericErr(e.to_string()))?;
// gramine private key
gramine_sgx_gen_private_key().await?;
// gramine manifest
gramine_manifest(&trusted_height.to_string(), &trusted_hash.to_string()).await?;
// gramine sign
gramine_sgx_sign().await?;
// Run quartz enclave and block
gramine_sgx().await?;
}
Ok(EnclaveStartResponse.into())
}
}
async fn run_enclave(
manifest_path: String,
mock_sgx: bool,
enclave_args: Vec<String>,
) -> Result<(), Error> {
let mut cargo = Command::new("cargo");
let command = cargo.args(["run", "--release", "--manifest-path", &manifest_path]);
if mock_sgx {
debug!("Running with mock-sgx enabled");
command.arg("--features=mock-sgx");
}
command.arg("--");
command.args(enclave_args);
println!("command: {:?}", command);
info!("🚧 Running enclave ...");
let status = command
.status()
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() {
return Err(Error::GenericErr(format!(
"Couldn't build enclave. {:?}",
status
)));
}
Ok(())
}
async fn gramine_sgx_gen_private_key() -> Result<(), Error> {
// Launch the gramine-sgx-gen-private-key command
Command::new("gramine-sgx-gen-private-key")
.output()
.await
.map_err(|e| {
Error::GenericErr(format!(
"Failed to execute gramine-sgx-gen-private-key: {}",
e
))
})?;
// Continue regardless of error
// > /dev/null 2>&1 || : # may fail
Ok(())
}
async fn gramine_manifest(trusted_height: &str, trusted_hash: &str) -> Result<(), Error> {
let current_dir = env::current_dir().map_err(|e| Error::GenericErr(e.to_string()))?;
let host = target_lexicon::HOST;
let arch_libdir = format!(
"/lib/{}-{}-{}",
host.architecture, host.operating_system, host.environment
);
let ra_client_spid = "51CAF5A48B450D624AEFE3286D314894";
let home_dir = dirs::home_dir()
.ok_or(Error::GenericErr("home dir not set".to_string()))?
.display()
.to_string();
let status = Command::new("gramine-manifest")
.arg("-Dlog_level=error")
.arg(format!("-Dhome={}", home_dir))
.arg(format!("-Darch_libdir={}", arch_libdir))
.arg("-Dra_type=epid")
.arg(format!("-Dra_client_spid={}", ra_client_spid))
.arg("-Dra_client_linkable=1")
.arg(format!("-Dquartz_dir={}", current_dir.display()))
.arg(format!("-Dtrusted_height={}", trusted_height))
.arg(format!("-Dtrusted_hash={}", trusted_hash))
.arg("quartz.manifest.template")
.arg("quartz.manifest")
.status()
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() {
return Err(Error::GenericErr(format!(
"Couldn't run gramine manifest. {:?}",
status
)));
}
Ok(())
}
async fn gramine_sgx_sign() -> Result<(), Error> {
let status = Command::new("gramine-sgx-sign")
.arg("--manifest")
.arg("quartz.manifest")
.arg("--output")
.arg("quartz.manifest.sgx")
.status()
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() {
return Err(Error::GenericErr(format!(
"gramine-sgx-sign command failed. {:?}",
status
)));
}
Ok(())
}
async fn gramine_sgx() -> Result<(), Error> {
let status = Command::new("gramine-sgx")
.arg("./quartz")
.status()
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;
if !status.success() {
return Err(Error::GenericErr(format!(
"gramine-sgx-sign command failed. {:?}",
status
)));
}
Ok(())
}

View file

@ -1,4 +1,4 @@
use std::{env::current_dir, fs, path::Path, str::FromStr};
use std::{env::current_dir, fs, str::FromStr};
use anyhow::anyhow;
use async_trait::async_trait;
@ -8,7 +8,6 @@ use futures_util::stream::StreamExt;
use reqwest::Url;
use serde::Serialize;
use serde_json::json;
use tendermint::{block::Height, Hash};
use tendermint_rpc::{query::EventType, HttpClient, SubscriptionClient, WebSocketClient};
use tm_prover::{config::Config as TmProverConfig, prover::prove};
use tracing::{debug, info, trace};
@ -19,7 +18,10 @@ use super::utils::{
};
use crate::{
error::Error,
handler::{utils::types::RelayMessage, Handler},
handler::{
utils::{helpers::read_hash_height, types::RelayMessage},
Handler,
},
request::handshake::HandshakeRequest,
response::{handshake::HandshakeResponse, Response},
Config,
@ -185,13 +187,3 @@ async fn two_block_waitoor(wsurl: &str) -> Result<(), anyhow::Error> {
Ok(())
}
async fn read_hash_height(base_path: &Path) -> Result<(Height, Hash), anyhow::Error> {
let height_path = base_path.join("trusted.height");
let trusted_height: Height = fs::read_to_string(height_path.as_path())?.trim().parse()?;
let hash_path = base_path.join("trusted.hash");
let trusted_hash: Hash = fs::read_to_string(hash_path.as_path())?.trim().parse()?;
Ok((trusted_height, trusted_hash))
}

View file

@ -5,10 +5,11 @@ use cosmrs::{AccountId, ErrorReport};
use regex::Regex;
use serde::de::DeserializeOwned;
use subtle_encoding::bech32::decode as bech32_decode;
use tendermint::Hash;
use tendermint::{block::Height, Hash};
use tendermint_rpc::{
endpoint::tx::Response as TmTxResponse, error::ErrorDetail, Client, HttpClient,
};
use tokio::fs;
use tracing::debug;
use super::types::RelayMessage;
@ -85,3 +86,19 @@ pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxRespon
}
}
}
pub async fn read_hash_height(base_path: &Path) -> Result<(Height, Hash), anyhow::Error> {
let height_path = base_path.join("trusted.height");
let trusted_height: Height = fs::read_to_string(height_path.as_path())
.await?
.trim()
.parse()?;
let hash_path = base_path.join("trusted.hash");
let trusted_hash: Hash = fs::read_to_string(hash_path.as_path())
.await?
.trim()
.parse()?;
Ok((trusted_height, trusted_hash))
}

View file

@ -26,11 +26,11 @@ use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
use crate::{cli::Cli, handler::Handler, request::Request};
const BANNER: &str = r"
________ ___ ___ ________ ________ _________ ________
________ ___ ___ ________ ________ __________ ________
|\ __ \ |\ \|\ \ |\ __ \ |\ __ \ |\___ ___\ |\_____ \
\ \ \|\ \ \ \ \\\ \ \ \ \|\ \ \ \ \|\ \ \|___ \ \_| \|___/ /|
\ \ \\\ \ \ \ \\\ \ \ \ __ \ \ \ _ _\ \ \ \ / / /
\ \ \\\ \ \ \ \\\ \ \ \ \ \ \ \ \ \\ \| \ \ \ / /_/__
\ \ \\\ \ \ \ \\\ \ \ \ \ \ \ \ \ \\ \ \ \ \ / /_/__
\ \_____ \ \ \_______\ \ \__\ \__\ \ \__\\ _\ \ \__\ |\________\
\|___| \__\ \|_______| \|__|\|__| \|__|\|__| \|__| \|_______|
\|__|

View file

@ -5,13 +5,15 @@ use crate::{
error::Error,
request::{
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
enclave_build::EnclaveBuildRequest, handshake::HandshakeRequest, init::InitRequest,
enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
handshake::HandshakeRequest, init::InitRequest,
},
};
pub mod contract_build;
pub mod contract_deploy;
pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
pub mod init;
@ -22,6 +24,7 @@ pub enum Request {
ContractBuild(ContractBuildRequest),
ContractDeploy(ContractDeployRequest),
EnclaveBuild(EnclaveBuildRequest),
EnclaveStart(EnclaveStartRequest),
}
impl TryFrom<Command> for Request {
@ -49,7 +52,7 @@ impl TryFrom<Command> for Request {
}
.into()),
Command::Contract { contract_command } => contract_command.try_into(),
Command::Enclave { enclave_command } => Ok(enclave_command.into()),
Command::Enclave { enclave_command } => enclave_command.try_into(),
}
}
}
@ -60,7 +63,6 @@ impl Request {
if !path.is_dir() {
return Err(Error::PathNotDir(format!("{}", path.display())));
}
Ok(path)
} else {
Ok(current_dir().map_err(|e| Error::GenericErr(e.to_string()))?)
@ -107,11 +109,19 @@ impl TryFrom<ContractCommand> for Request {
}
}
impl From<EnclaveCommand> for Request {
fn from(cmd: EnclaveCommand) -> Request {
impl TryFrom<EnclaveCommand> for Request {
type Error = Error;
fn try_from(cmd: EnclaveCommand) -> Result<Request, Error> {
match cmd {
EnclaveCommand::Build { manifest_path } => EnclaveBuildRequest { manifest_path }.into(),
EnclaveCommand::Start { path: _ } => todo!(),
EnclaveCommand::Build { manifest_path } => {
Ok(EnclaveBuildRequest { manifest_path }.into())
}
EnclaveCommand::Start { app_dir, chain_id } => Ok(EnclaveStartRequest {
app_dir: Self::path_checked(app_dir)?,
chain_id,
}
.into()),
}
}
}

View file

@ -0,0 +1,15 @@
use std::path::PathBuf;
use crate::request::Request;
#[derive(Clone, Debug)]
pub struct EnclaveStartRequest {
pub app_dir: PathBuf,
pub chain_id: String,
}
impl From<EnclaveStartRequest> for Request {
fn from(request: EnclaveStartRequest) -> Self {
Self::EnclaveStart(request)
}
}

View file

@ -2,12 +2,14 @@ use serde::Serialize;
use crate::response::{
contract_build::ContractBuildResponse, contract_deploy::ContractDeployResponse,
enclave_build::EnclaveBuildResponse, handshake::HandshakeResponse, init::InitResponse,
enclave_build::EnclaveBuildResponse, enclave_start::EnclaveStartResponse,
handshake::HandshakeResponse, init::InitResponse,
};
pub mod contract_build;
pub mod contract_deploy;
pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
pub mod init;
@ -18,4 +20,5 @@ pub enum Response {
ContractBuild(ContractBuildResponse),
ContractDeploy(ContractDeployResponse),
EnclaveBuild(EnclaveBuildResponse),
EnclaveStart(EnclaveStartResponse),
}

View file

@ -0,0 +1,12 @@
use serde::Serialize;
use crate::response::Response;
#[derive(Clone, Debug, Serialize)]
pub struct EnclaveStartResponse;
impl From<EnclaveStartResponse> for Response {
fn from(response: EnclaveStartResponse) -> Self {
Self::EnclaveStart(response)
}
}