WIP quartz sessions (#48)
This commit is contained in:
commit
a4a690b9cb
19 changed files with 2819 additions and 273 deletions
1663
Cargo.lock
generated
1663
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
1
enclaves/quartz/.gitignore
vendored
Normal file
1
enclaves/quartz/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
quartz.sig
|
23
enclaves/quartz/Cargo.toml
Normal file
23
enclaves/quartz/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "quartz-enclave"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
|
color-eyre = "0.6.2"
|
||||||
|
cosmwasm-std = "1.4.0"
|
||||||
|
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
|
||||||
|
prost = "0.12"
|
||||||
|
rand = "0.8.5"
|
||||||
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
|
serde_json = "1.0.94"
|
||||||
|
tendermint = "0.34.0"
|
||||||
|
tendermint-light-client = "0.34.0"
|
||||||
|
tonic = "0.11"
|
||||||
|
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
|
quartz-cw = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
|
||||||
|
quartz-proto = { path = "../../utils/quartz-proto" }
|
||||||
|
quartz-relayer = { path = "../../utils/quartz-relayer" }
|
||||||
|
quartz-tee-ra = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
|
30
enclaves/quartz/README.md
Normal file
30
enclaves/quartz/README.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
## Quartz enclave
|
||||||
|
|
||||||
|
### Enclave usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gramine-sgx-gen-private-key
|
||||||
|
|
||||||
|
CARGO_TARGET_DIR=./target cargo build --release
|
||||||
|
|
||||||
|
gramine-manifest \
|
||||||
|
-Dlog_level="error" \
|
||||||
|
-Dhome=${HOME} \
|
||||||
|
-Darch_libdir="/lib/$(gcc -dumpmachine)" \
|
||||||
|
-Dra_type="epid" \
|
||||||
|
-Dra_client_spid="51CAF5A48B450D624AEFE3286D314894" \
|
||||||
|
-Dra_client_linkable=1 \
|
||||||
|
-Dquartz_dir="$(pwd)" \
|
||||||
|
quartz.manifest.template quartz.manifest
|
||||||
|
|
||||||
|
gramine-sgx-sign --manifest quartz.manifest --output quartz.manifest.sgx
|
||||||
|
gramine-sgx ./quartz
|
||||||
|
```
|
||||||
|
|
||||||
|
### CLI usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run -- --chain-id testing \
|
||||||
|
--trusted-height 1 \
|
||||||
|
--trusted-hash "A1D115BA3A5E9FCC12ED68A9D8669159E9085F6F96EC26619F5C7CEB4EE02869"
|
||||||
|
```
|
57
enclaves/quartz/quartz.manifest.template
Normal file
57
enclaves/quartz/quartz.manifest.template
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Quartz manifest file
|
||||||
|
|
||||||
|
loader.entrypoint = "file:{{ gramine.libos }}"
|
||||||
|
libos.entrypoint = "{{ quartz_dir }}/target/release/quartz-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.argv = ["quartz-enclave",
|
||||||
|
"--chain-id", "testing",
|
||||||
|
"--trusted-height", "1",
|
||||||
|
"--trusted-hash", "A1D115BA3A5E9FCC12ED68A9D8669159E9085F6F96EC26619F5C7CEB4EE02869"]
|
||||||
|
|
||||||
|
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/quartz-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
|
62
enclaves/quartz/src/attestor.rs
Normal file
62
enclaves/quartz/src/attestor.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use std::{
|
||||||
|
fs::{read, File},
|
||||||
|
io::{Error as IoError, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
use quartz_cw::{
|
||||||
|
msg::execute::attested::HasUserData,
|
||||||
|
state::{MrEnclave, UserData},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait Attestor {
|
||||||
|
type Error: ToString;
|
||||||
|
|
||||||
|
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error>;
|
||||||
|
|
||||||
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub struct EpidAttestor;
|
||||||
|
|
||||||
|
impl Attestor for EpidAttestor {
|
||||||
|
type Error = IoError;
|
||||||
|
|
||||||
|
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error> {
|
||||||
|
let user_data = user_data.user_data();
|
||||||
|
let mut user_report_data = File::create("/dev/attestation/user_report_data")?;
|
||||||
|
user_report_data.write_all(user_data.as_slice())?;
|
||||||
|
user_report_data.flush()?;
|
||||||
|
read("/dev/attestation/quote")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error> {
|
||||||
|
let quote = self.quote(NullUserData)?;
|
||||||
|
Ok(quote[112..(112 + 32)]
|
||||||
|
.try_into()
|
||||||
|
.expect("hardcoded array size"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub struct MockAttestor;
|
||||||
|
|
||||||
|
impl Attestor for MockAttestor {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn quote(&self, _user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error> {
|
||||||
|
Ok([0u8; 32])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NullUserData;
|
||||||
|
|
||||||
|
impl HasUserData for NullUserData {
|
||||||
|
fn user_data(&self) -> UserData {
|
||||||
|
[0u8; 64]
|
||||||
|
}
|
||||||
|
}
|
52
enclaves/quartz/src/cli.rs
Normal file
52
enclaves/quartz/src/cli.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use tendermint::Hash;
|
||||||
|
use tendermint_light_client::types::{Height, TrustThreshold};
|
||||||
|
|
||||||
|
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
||||||
|
if let Some((l, r)) = s.split_once('/') {
|
||||||
|
TrustThreshold::new(l.parse()?, r.parse()?).map_err(Into::into)
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"invalid trust threshold: {s}, format must be X/Y where X and Y are integers"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
/// RPC server address
|
||||||
|
#[clap(long, default_value = "127.0.0.1:11090")]
|
||||||
|
pub rpc_addr: SocketAddr,
|
||||||
|
|
||||||
|
/// Identifier of the chain
|
||||||
|
#[clap(long)]
|
||||||
|
pub chain_id: String,
|
||||||
|
|
||||||
|
/// Height of the trusted header (AKA root-of-trust)
|
||||||
|
#[clap(long)]
|
||||||
|
pub trusted_height: Height,
|
||||||
|
|
||||||
|
/// Hash of the trusted header (AKA root-of-trust)
|
||||||
|
#[clap(long)]
|
||||||
|
pub trusted_hash: Hash,
|
||||||
|
|
||||||
|
/// Trust threshold
|
||||||
|
#[clap(long, value_parser = parse_trust_threshold, default_value_t = TrustThreshold::TWO_THIRDS)]
|
||||||
|
pub trust_threshold: TrustThreshold,
|
||||||
|
|
||||||
|
/// Trusting period, in seconds (default: two weeks)
|
||||||
|
#[clap(long, default_value = "1209600")]
|
||||||
|
pub trusting_period: u64,
|
||||||
|
|
||||||
|
/// Maximum clock drift, in seconds
|
||||||
|
#[clap(long, default_value = "5")]
|
||||||
|
pub max_clock_drift: u64,
|
||||||
|
|
||||||
|
/// Maximum block lag, in seconds
|
||||||
|
#[clap(long, default_value = "5")]
|
||||||
|
pub max_block_lag: u64,
|
||||||
|
}
|
64
enclaves/quartz/src/main.rs
Normal file
64
enclaves/quartz/src/main.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(
|
||||||
|
clippy::checked_conversions,
|
||||||
|
clippy::panic,
|
||||||
|
clippy::panic_in_result_fn,
|
||||||
|
missing_docs,
|
||||||
|
trivial_casts,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
rust_2018_idioms,
|
||||||
|
unused_lifetimes,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_qualifications
|
||||||
|
)]
|
||||||
|
|
||||||
|
mod attestor;
|
||||||
|
mod cli;
|
||||||
|
mod server;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use quartz_cw::state::{Config, LightClientOpts};
|
||||||
|
use quartz_proto::quartz::core_server::CoreServer;
|
||||||
|
use tonic::transport::Server;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
attestor::{Attestor, EpidAttestor},
|
||||||
|
cli::Cli,
|
||||||
|
server::CoreService,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = Cli::parse();
|
||||||
|
|
||||||
|
let light_client_opts = LightClientOpts::new(
|
||||||
|
args.chain_id,
|
||||||
|
args.trusted_height.into(),
|
||||||
|
Vec::from(args.trusted_hash)
|
||||||
|
.try_into()
|
||||||
|
.expect("invalid trusted hash"),
|
||||||
|
(
|
||||||
|
args.trust_threshold.numerator(),
|
||||||
|
args.trust_threshold.denominator(),
|
||||||
|
),
|
||||||
|
args.trusting_period,
|
||||||
|
args.max_clock_drift,
|
||||||
|
args.max_block_lag,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let config = Config::new(
|
||||||
|
EpidAttestor.mr_enclave()?,
|
||||||
|
Duration::from_secs(30 * 24 * 60),
|
||||||
|
light_client_opts,
|
||||||
|
);
|
||||||
|
|
||||||
|
Server::builder()
|
||||||
|
.add_service(CoreServer::new(CoreService::new(config, EpidAttestor)))
|
||||||
|
.serve(args.rpc_addr)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
101
enclaves/quartz/src/server.rs
Normal file
101
enclaves/quartz/src/server.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use k256::ecdsa::SigningKey;
|
||||||
|
use quartz_cw::{
|
||||||
|
msg::{
|
||||||
|
execute::{session_create::SessionCreate, session_set_pub_key::SessionSetPubKey},
|
||||||
|
instantiate::CoreInstantiate,
|
||||||
|
},
|
||||||
|
state::{Config, Nonce},
|
||||||
|
};
|
||||||
|
use quartz_proto::quartz::{
|
||||||
|
core_server::Core, InstantiateRequest as RawInstantiateRequest,
|
||||||
|
InstantiateResponse as RawInstantiateResponse, SessionCreateRequest as RawSessionCreateRequest,
|
||||||
|
SessionCreateResponse as RawSessionCreateResponse,
|
||||||
|
SessionSetPubKeyRequest as RawSessionSetPubKeyRequest,
|
||||||
|
SessionSetPubKeyResponse as RawSessionSetPubKeyResponse,
|
||||||
|
};
|
||||||
|
use quartz_relayer::types::{InstantiateResponse, SessionCreateResponse, SessionSetPubKeyResponse};
|
||||||
|
use rand::Rng;
|
||||||
|
use tonic::{Request, Response, Status};
|
||||||
|
|
||||||
|
use crate::attestor::Attestor;
|
||||||
|
|
||||||
|
type TonicResult<T> = Result<T, Status>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CoreService<A> {
|
||||||
|
config: Config,
|
||||||
|
nonce: Arc<Mutex<Nonce>>,
|
||||||
|
attestor: A,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A> CoreService<A>
|
||||||
|
where
|
||||||
|
A: Attestor,
|
||||||
|
{
|
||||||
|
pub fn new(config: Config, attestor: A) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
nonce: Arc::new(Mutex::new([0u8; 32])),
|
||||||
|
attestor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tonic::async_trait]
|
||||||
|
impl<A> Core for CoreService<A>
|
||||||
|
where
|
||||||
|
A: Attestor + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
async fn instantiate(
|
||||||
|
&self,
|
||||||
|
_request: Request<RawInstantiateRequest>,
|
||||||
|
) -> TonicResult<Response<RawInstantiateResponse>> {
|
||||||
|
let core_instantiate_msg = CoreInstantiate::new(self.config.clone());
|
||||||
|
|
||||||
|
let quote = self
|
||||||
|
.attestor
|
||||||
|
.quote(core_instantiate_msg)
|
||||||
|
.map_err(|e| Status::internal(e.to_string()))?;
|
||||||
|
|
||||||
|
let response = InstantiateResponse::new(self.config.clone(), quote);
|
||||||
|
Ok(Response::new(response.into()))
|
||||||
|
}
|
||||||
|
async fn session_create(
|
||||||
|
&self,
|
||||||
|
_request: Request<RawSessionCreateRequest>,
|
||||||
|
) -> TonicResult<Response<RawSessionCreateResponse>> {
|
||||||
|
let mut nonce = self.nonce.lock().unwrap();
|
||||||
|
*nonce = rand::thread_rng().gen::<Nonce>();
|
||||||
|
|
||||||
|
let session_create_msg = SessionCreate::new(*nonce);
|
||||||
|
|
||||||
|
let quote = self
|
||||||
|
.attestor
|
||||||
|
.quote(session_create_msg)
|
||||||
|
.map_err(|e| Status::internal(e.to_string()))?;
|
||||||
|
|
||||||
|
let response = SessionCreateResponse::new(*nonce, quote);
|
||||||
|
Ok(Response::new(response.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn session_set_pub_key(
|
||||||
|
&self,
|
||||||
|
_request: Request<RawSessionSetPubKeyRequest>,
|
||||||
|
) -> TonicResult<Response<RawSessionSetPubKeyResponse>> {
|
||||||
|
let nonce = self.nonce.lock().unwrap();
|
||||||
|
let sk = SigningKey::random(&mut rand::thread_rng());
|
||||||
|
let pk = sk.verifying_key();
|
||||||
|
|
||||||
|
let session_set_pub_key_msg = SessionSetPubKey::new(*nonce, *pk);
|
||||||
|
|
||||||
|
let quote = self
|
||||||
|
.attestor
|
||||||
|
.quote(session_set_pub_key_msg)
|
||||||
|
.map_err(|e| Status::internal(e.to_string()))?;
|
||||||
|
|
||||||
|
let response = SessionSetPubKeyResponse::new(*nonce, *pk, quote);
|
||||||
|
Ok(Response::new(response.into()))
|
||||||
|
}
|
||||||
|
}
|
11
utils/quartz-proto/Cargo.toml
Normal file
11
utils/quartz-proto/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "quartz-proto"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
prost = "0.12"
|
||||||
|
tonic = "0.11"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tonic-build = "0.11"
|
6
utils/quartz-proto/build.rs
Normal file
6
utils/quartz-proto/build.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
tonic_build::configure()
|
||||||
|
.out_dir("src/prost")
|
||||||
|
.compile(&["proto/quartz.proto"], &["proto"])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
27
utils/quartz-proto/proto/quartz.proto
Normal file
27
utils/quartz-proto/proto/quartz.proto
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package quartz;
|
||||||
|
|
||||||
|
service Core {
|
||||||
|
rpc Instantiate (InstantiateRequest) returns (InstantiateResponse) {}
|
||||||
|
rpc SessionCreate (SessionCreateRequest) returns (SessionCreateResponse) {}
|
||||||
|
rpc SessionSetPubKey (SessionSetPubKeyRequest) returns (SessionSetPubKeyResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message InstantiateRequest {}
|
||||||
|
|
||||||
|
message InstantiateResponse {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SessionCreateRequest {}
|
||||||
|
|
||||||
|
message SessionCreateResponse {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SessionSetPubKeyRequest {}
|
||||||
|
|
||||||
|
message SessionSetPubKeyResponse {
|
||||||
|
string message = 1;
|
||||||
|
}
|
5
utils/quartz-proto/src/lib.rs
Normal file
5
utils/quartz-proto/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#![allow(clippy::unwrap_used, unused_qualifications)]
|
||||||
|
|
||||||
|
pub mod quartz {
|
||||||
|
include!(concat!("prost/", "quartz.rs"));
|
||||||
|
}
|
470
utils/quartz-proto/src/prost/quartz.rs
Normal file
470
utils/quartz-proto/src/prost/quartz.rs
Normal file
|
@ -0,0 +1,470 @@
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct InstantiateRequest {}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct InstantiateResponse {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub message: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SessionCreateRequest {}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SessionCreateResponse {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub message: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SessionSetPubKeyRequest {}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SessionSetPubKeyResponse {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub message: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// Generated client implementations.
|
||||||
|
pub mod core_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 CoreClient<T> {
|
||||||
|
inner: tonic::client::Grpc<T>,
|
||||||
|
}
|
||||||
|
impl CoreClient<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> CoreClient<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,
|
||||||
|
) -> CoreClient<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,
|
||||||
|
{
|
||||||
|
CoreClient::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 instantiate(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::InstantiateRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::InstantiateResponse>,
|
||||||
|
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("/quartz.Core/Instantiate");
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut().insert(GrpcMethod::new("quartz.Core", "Instantiate"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
|
pub async fn session_create(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::SessionCreateRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::SessionCreateResponse>,
|
||||||
|
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(
|
||||||
|
"/quartz.Core/SessionCreate",
|
||||||
|
);
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut().insert(GrpcMethod::new("quartz.Core", "SessionCreate"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
|
pub async fn session_set_pub_key(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::SessionSetPubKeyRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::SessionSetPubKeyResponse>,
|
||||||
|
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(
|
||||||
|
"/quartz.Core/SessionSetPubKey",
|
||||||
|
);
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut()
|
||||||
|
.insert(GrpcMethod::new("quartz.Core", "SessionSetPubKey"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Generated server implementations.
|
||||||
|
pub mod core_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 CoreServer.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Core: Send + Sync + 'static {
|
||||||
|
async fn instantiate(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::InstantiateRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::InstantiateResponse>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
async fn session_create(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::SessionCreateRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::SessionCreateResponse>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
async fn session_set_pub_key(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::SessionSetPubKeyRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::SessionSetPubKeyResponse>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CoreServer<T: Core> {
|
||||||
|
inner: _Inner<T>,
|
||||||
|
accept_compression_encodings: EnabledCompressionEncodings,
|
||||||
|
send_compression_encodings: EnabledCompressionEncodings,
|
||||||
|
max_decoding_message_size: Option<usize>,
|
||||||
|
max_encoding_message_size: Option<usize>,
|
||||||
|
}
|
||||||
|
struct _Inner<T>(Arc<T>);
|
||||||
|
impl<T: Core> CoreServer<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
Self::from_arc(Arc::new(inner))
|
||||||
|
}
|
||||||
|
pub fn from_arc(inner: Arc<T>) -> Self {
|
||||||
|
let inner = _Inner(inner);
|
||||||
|
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 CoreServer<T>
|
||||||
|
where
|
||||||
|
T: Core,
|
||||||
|
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 {
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
match req.uri().path() {
|
||||||
|
"/quartz.Core/Instantiate" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct InstantiateSvc<T: Core>(pub Arc<T>);
|
||||||
|
impl<T: Core> tonic::server::UnaryService<super::InstantiateRequest>
|
||||||
|
for InstantiateSvc<T> {
|
||||||
|
type Response = super::InstantiateResponse;
|
||||||
|
type Future = BoxFuture<
|
||||||
|
tonic::Response<Self::Response>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::InstantiateRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move {
|
||||||
|
<T as Core>::instantiate(&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 inner = inner.0;
|
||||||
|
let method = InstantiateSvc(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)
|
||||||
|
}
|
||||||
|
"/quartz.Core/SessionCreate" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct SessionCreateSvc<T: Core>(pub Arc<T>);
|
||||||
|
impl<
|
||||||
|
T: Core,
|
||||||
|
> tonic::server::UnaryService<super::SessionCreateRequest>
|
||||||
|
for SessionCreateSvc<T> {
|
||||||
|
type Response = super::SessionCreateResponse;
|
||||||
|
type Future = BoxFuture<
|
||||||
|
tonic::Response<Self::Response>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::SessionCreateRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move {
|
||||||
|
<T as Core>::session_create(&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 inner = inner.0;
|
||||||
|
let method = SessionCreateSvc(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)
|
||||||
|
}
|
||||||
|
"/quartz.Core/SessionSetPubKey" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct SessionSetPubKeySvc<T: Core>(pub Arc<T>);
|
||||||
|
impl<
|
||||||
|
T: Core,
|
||||||
|
> tonic::server::UnaryService<super::SessionSetPubKeyRequest>
|
||||||
|
for SessionSetPubKeySvc<T> {
|
||||||
|
type Response = super::SessionSetPubKeyResponse;
|
||||||
|
type Future = BoxFuture<
|
||||||
|
tonic::Response<Self::Response>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::SessionSetPubKeyRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move {
|
||||||
|
<T as Core>::session_set_pub_key(&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 inner = inner.0;
|
||||||
|
let method = SessionSetPubKeySvc(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", "12")
|
||||||
|
.header("content-type", "application/grpc")
|
||||||
|
.body(empty_body())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Core> Clone for CoreServer<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: Core> Clone for _Inner<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(Arc::clone(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Core> tonic::server::NamedService for CoreServer<T> {
|
||||||
|
const NAME: &'static str = "quartz.Core";
|
||||||
|
}
|
||||||
|
}
|
25
utils/quartz-relayer/Cargo.toml
Normal file
25
utils/quartz-relayer/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "quartz-relayer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
|
cosmos-sdk-proto = "0.16.0"
|
||||||
|
cosmrs = { version = "=0.11.0", features = ["cosmwasm"] }
|
||||||
|
cosmwasm-std = "1.4.0"
|
||||||
|
displaydoc = { version = "0.2.3", default-features = false }
|
||||||
|
ecies = { version = "0.2.6", default-features = false, features = ["pure"] }
|
||||||
|
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
|
||||||
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
|
serde_json = "1.0.94"
|
||||||
|
subtle-encoding = { version = "0.5.1", features = ["bech32-preview"] }
|
||||||
|
tempfile = "3"
|
||||||
|
tendermint = { version = "0.29.0", features = ["secp256k1"] }
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
tonic = "=0.8.3"
|
||||||
|
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
|
quartz-cw = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
|
||||||
|
quartz-tee-ra = { git = "ssh://git@github.com/informalsystems/bisenzone-cw-mvp.git", branch = "hu55a1n1/11-use-quartz" }
|
||||||
|
quartz-proto = { path = "../../utils/quartz-proto" }
|
53
utils/quartz-relayer/src/cli.rs
Normal file
53
utils/quartz-relayer/src/cli.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmrs::{tendermint::chain::Id, AccountId};
|
||||||
|
use displaydoc::Display;
|
||||||
|
use subtle_encoding::{bech32::decode as bech32_decode, Error as Bech32DecodeError};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tonic::transport::Endpoint;
|
||||||
|
|
||||||
|
#[derive(Display, Error, Debug)]
|
||||||
|
pub enum AddressError {
|
||||||
|
/// Address is not bech32 encoded
|
||||||
|
NotBech32Encoded(#[source] Bech32DecodeError),
|
||||||
|
/// Human readable part mismatch (expected `wasm`, found {0})
|
||||||
|
HumanReadableMismatch(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
/// RPC server address
|
||||||
|
#[clap(long, default_value = "http://localhost:11090")]
|
||||||
|
pub enclave_addr: Endpoint,
|
||||||
|
|
||||||
|
/// Blockchain node gRPC URL
|
||||||
|
#[arg(short, long, default_value = "tcp://127.0.0.1:9090")]
|
||||||
|
pub node_addr: Endpoint,
|
||||||
|
|
||||||
|
/// Chain-id of MTCS chain
|
||||||
|
#[arg(long, default_value = "testing")]
|
||||||
|
pub chain_id: Id,
|
||||||
|
|
||||||
|
/// Smart contract address
|
||||||
|
#[arg(short, long, value_parser = wasm_address)]
|
||||||
|
pub contract: AccountId,
|
||||||
|
|
||||||
|
/// Path to TSP secret key file
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub secret: PathBuf,
|
||||||
|
|
||||||
|
/// Gas limit for the set-offs submission transaction
|
||||||
|
#[arg(long, default_value = "900000000")]
|
||||||
|
pub gas_limit: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wasm_address(address_str: &str) -> Result<AccountId, AddressError> {
|
||||||
|
let (hr, _) = bech32_decode(address_str).map_err(AddressError::NotBech32Encoded)?;
|
||||||
|
if hr != "wasm" {
|
||||||
|
return Err(AddressError::HumanReadableMismatch(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(address_str.parse().unwrap())
|
||||||
|
}
|
1
utils/quartz-relayer/src/lib.rs
Normal file
1
utils/quartz-relayer/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod types;
|
178
utils/quartz-relayer/src/main.rs
Normal file
178
utils/quartz-relayer/src/main.rs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
mod cli;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fs::{read_to_string, File},
|
||||||
|
io::{Read, Write},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use cosmos_sdk_proto::{
|
||||||
|
cosmos::{
|
||||||
|
auth::v1beta1::{
|
||||||
|
query_client::QueryClient as AuthQueryClient, BaseAccount as RawBaseAccount,
|
||||||
|
QueryAccountRequest,
|
||||||
|
},
|
||||||
|
tx::v1beta1::{service_client::ServiceClient, BroadcastMode, BroadcastTxRequest},
|
||||||
|
},
|
||||||
|
traits::Message,
|
||||||
|
Any,
|
||||||
|
};
|
||||||
|
use cosmrs::{
|
||||||
|
auth::BaseAccount,
|
||||||
|
cosmwasm::MsgExecuteContract,
|
||||||
|
crypto::secp256k1::{SigningKey, VerifyingKey},
|
||||||
|
tendermint::{account::Id as TmAccountId, chain::Id as TmChainId},
|
||||||
|
tx,
|
||||||
|
tx::{Fee, Msg, SignDoc, SignerInfo},
|
||||||
|
AccountId, Coin,
|
||||||
|
};
|
||||||
|
use ecies::{PublicKey, SecretKey};
|
||||||
|
use quartz_cw::msg::{
|
||||||
|
execute::attested::{Attested, EpidAttestation},
|
||||||
|
instantiate::{CoreInstantiate, RawInstantiate},
|
||||||
|
InstantiateMsg,
|
||||||
|
};
|
||||||
|
use quartz_proto::quartz::{core_client::CoreClient, InstantiateRequest};
|
||||||
|
use quartz_relayer::types::InstantiateResponse;
|
||||||
|
use quartz_tee_ra::IASReport;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use subtle_encoding::base64;
|
||||||
|
use tendermint::public_key::Secp256k1 as TmPublicKey;
|
||||||
|
|
||||||
|
use crate::cli::Cli;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = Cli::parse();
|
||||||
|
|
||||||
|
let mut client = CoreClient::connect(args.enclave_addr.uri().to_string()).await?;
|
||||||
|
let response = client.instantiate(InstantiateRequest {}).await?;
|
||||||
|
let response: InstantiateResponse = response.into_inner().try_into()?;
|
||||||
|
let (config, quote) = response.into_message().into_tuple();
|
||||||
|
|
||||||
|
let ias_report = gramine_sgx_ias_report("e)?;
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string(&ias_report).expect("infallible serializer")
|
||||||
|
);
|
||||||
|
let ias_report: IASReport = serde_json::from_str(&ias_report.to_string())?;
|
||||||
|
let attestation = EpidAttestation::new(ias_report);
|
||||||
|
let cw_instantiate_msg = Attested::new(CoreInstantiate::new(config), attestation);
|
||||||
|
|
||||||
|
// Read the TSP secret
|
||||||
|
let secret = {
|
||||||
|
let mut secret = Vec::new();
|
||||||
|
let mut tsp_sk_file = File::open(args.secret)?;
|
||||||
|
tsp_sk_file.read_to_end(secret.as_mut())?;
|
||||||
|
let secret = base64::decode(secret).unwrap();
|
||||||
|
SecretKey::parse_slice(&secret).unwrap()
|
||||||
|
};
|
||||||
|
let tm_pubkey = {
|
||||||
|
let pubkey = PublicKey::from_secret_key(&secret);
|
||||||
|
TmPublicKey::from_sec1_bytes(&pubkey.serialize()).unwrap()
|
||||||
|
};
|
||||||
|
let sender = {
|
||||||
|
let tm_key = TmAccountId::from(tm_pubkey);
|
||||||
|
AccountId::new("wasm", tm_key.as_bytes()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let msgs = vec![MsgExecuteContract {
|
||||||
|
sender: sender.clone(),
|
||||||
|
contract: args.contract.clone(),
|
||||||
|
msg: serde_json::to_string::<RawInstantiate>(&InstantiateMsg(cw_instantiate_msg).into())?
|
||||||
|
.into_bytes(),
|
||||||
|
funds: vec![],
|
||||||
|
}
|
||||||
|
.to_any()
|
||||||
|
.unwrap()];
|
||||||
|
|
||||||
|
let account = account_info(args.node_addr.uri().clone(), sender.clone()).await?;
|
||||||
|
let amount = Coin {
|
||||||
|
amount: 0u128,
|
||||||
|
denom: "cosm".parse()?,
|
||||||
|
};
|
||||||
|
let tx_bytes = tx_bytes(
|
||||||
|
&secret,
|
||||||
|
amount,
|
||||||
|
args.gas_limit,
|
||||||
|
tm_pubkey,
|
||||||
|
msgs,
|
||||||
|
account.sequence,
|
||||||
|
account.account_number,
|
||||||
|
&args.chain_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
send_tx(args.node_addr.uri().clone(), tx_bytes).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn account_info(
|
||||||
|
node: impl ToString,
|
||||||
|
address: impl ToString,
|
||||||
|
) -> Result<BaseAccount, Box<dyn Error>> {
|
||||||
|
let mut client = AuthQueryClient::connect(node.to_string()).await?;
|
||||||
|
let request = tonic::Request::new(QueryAccountRequest {
|
||||||
|
address: address.to_string(),
|
||||||
|
});
|
||||||
|
let response = client.account(request).await?;
|
||||||
|
let response = RawBaseAccount::decode(response.into_inner().account.unwrap().value.as_slice())?;
|
||||||
|
let account = BaseAccount::try_from(response)?;
|
||||||
|
Ok(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn tx_bytes(
|
||||||
|
secret: &SecretKey,
|
||||||
|
amount: Coin,
|
||||||
|
gas: u64,
|
||||||
|
tm_pubkey: VerifyingKey,
|
||||||
|
msgs: Vec<Any>,
|
||||||
|
sequence_number: u64,
|
||||||
|
account_number: u64,
|
||||||
|
chain_id: &TmChainId,
|
||||||
|
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let tx_body = tx::Body::new(msgs, "", 0u16);
|
||||||
|
let signer_info = SignerInfo::single_direct(Some(tm_pubkey.into()), sequence_number);
|
||||||
|
let auth_info = signer_info.auth_info(Fee::from_amount_and_gas(amount, gas));
|
||||||
|
let sign_doc = SignDoc::new(&tx_body, &auth_info, chain_id, account_number)?;
|
||||||
|
let tx_signed = sign_doc.sign(&SigningKey::from_bytes(&secret.serialize()).unwrap())?;
|
||||||
|
Ok(tx_signed.to_bytes()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_tx(node: impl ToString, tx_bytes: Vec<u8>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut client = ServiceClient::connect(node.to_string()).await?;
|
||||||
|
let request = tonic::Request::new(BroadcastTxRequest {
|
||||||
|
tx_bytes,
|
||||||
|
mode: BroadcastMode::Block.into(),
|
||||||
|
});
|
||||||
|
let _response = client.broadcast_tx(request).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gramine_sgx_ias_report(quote: &[u8]) -> Result<Value, Box<dyn Error>> {
|
||||||
|
let dir = tempfile::tempdir()?;
|
||||||
|
let quote_file_path = dir.path().join("test.quote");
|
||||||
|
let datareport_file_path = dir.path().join("datareport");
|
||||||
|
let datareportsig_file_path = dir.path().join("datareportsig");
|
||||||
|
|
||||||
|
let mut quote_file = File::create(quote_file_path.clone())?;
|
||||||
|
quote_file.write_all(quote)?;
|
||||||
|
|
||||||
|
let gramine_sgx_ias_request_output = Command::new("gramine-sgx-ias-request")
|
||||||
|
.arg("report")
|
||||||
|
.args(["-g", "51CAF5A48B450D624AEFE3286D314894"])
|
||||||
|
.args(["-k", "669244b3e6364b5888289a11d2a1726d"])
|
||||||
|
.args(["-q", "e_file_path.display().to_string()])
|
||||||
|
.args(["-r", &datareport_file_path.display().to_string()])
|
||||||
|
.args(["-s", &datareportsig_file_path.display().to_string()])
|
||||||
|
.output()?;
|
||||||
|
println!("{gramine_sgx_ias_request_output:?}");
|
||||||
|
|
||||||
|
let report = read_to_string(datareport_file_path)?;
|
||||||
|
let report_sig = read_to_string(datareportsig_file_path)?;
|
||||||
|
let ias_report = json!({"report": report, "reportsig": report_sig});
|
||||||
|
Ok(ias_report)
|
||||||
|
}
|
263
utils/quartz-relayer/src/types.rs
Normal file
263
utils/quartz-relayer/src/types.rs
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
use cosmwasm_std::{HexBinary, StdError};
|
||||||
|
use k256::ecdsa::VerifyingKey;
|
||||||
|
use quartz_cw::{
|
||||||
|
error::Error as QuartzCwError,
|
||||||
|
state::{Config, Nonce, RawConfig},
|
||||||
|
};
|
||||||
|
use quartz_proto::quartz::{
|
||||||
|
InstantiateResponse as RawInstantiateResponse,
|
||||||
|
SessionCreateResponse as RawSessionCreateResponse,
|
||||||
|
SessionSetPubKeyResponse as RawSessionSetPubKeyResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct InstantiateResponse {
|
||||||
|
message: InstantiateResponseMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstantiateResponse {
|
||||||
|
pub fn new(config: Config, quote: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
message: InstantiateResponseMsg { config, quote },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quote(&self) -> &[u8] {
|
||||||
|
&self.message.quote
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_message(self) -> InstantiateResponseMsg {
|
||||||
|
self.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawInstantiateResponse> for InstantiateResponse {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawInstantiateResponse) -> Result<Self, Self::Error> {
|
||||||
|
let raw_message: RawInstantiateResponseMsg = serde_json::from_str(&value.message)
|
||||||
|
.map_err(|e| StdError::parse_err("RawInstantiateResponseMsg", e))?;
|
||||||
|
Ok(Self {
|
||||||
|
message: raw_message.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InstantiateResponse> for RawInstantiateResponse {
|
||||||
|
fn from(value: InstantiateResponse) -> Self {
|
||||||
|
let raw_message: RawInstantiateResponseMsg = value.message.into();
|
||||||
|
Self {
|
||||||
|
message: serde_json::to_string(&raw_message).expect("infallible serializer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct InstantiateResponseMsg {
|
||||||
|
config: Config,
|
||||||
|
quote: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstantiateResponseMsg {
|
||||||
|
pub fn into_tuple(self) -> (Config, Vec<u8>) {
|
||||||
|
(self.config, self.quote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RawInstantiateResponseMsg {
|
||||||
|
config: RawConfig,
|
||||||
|
quote: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawInstantiateResponseMsg> for InstantiateResponseMsg {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawInstantiateResponseMsg) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
config: value.config.try_into()?,
|
||||||
|
quote: value.quote.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InstantiateResponseMsg> for RawInstantiateResponseMsg {
|
||||||
|
fn from(value: InstantiateResponseMsg) -> Self {
|
||||||
|
Self {
|
||||||
|
config: value.config.into(),
|
||||||
|
quote: value.quote.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct SessionCreateResponse {
|
||||||
|
message: SessionCreateResponseMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionCreateResponse {
|
||||||
|
pub fn new(nonce: Nonce, quote: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
message: SessionCreateResponseMsg { nonce, quote },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quote(&self) -> &[u8] {
|
||||||
|
&self.message.quote
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_message(self) -> SessionCreateResponseMsg {
|
||||||
|
self.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawSessionCreateResponse> for SessionCreateResponse {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawSessionCreateResponse) -> Result<Self, Self::Error> {
|
||||||
|
let raw_message: RawSessionCreateResponseMsg = serde_json::from_str(&value.message)
|
||||||
|
.map_err(|e| StdError::parse_err("RawSessionCreateResponseMsg", e))?;
|
||||||
|
Ok(Self {
|
||||||
|
message: raw_message.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SessionCreateResponse> for RawSessionCreateResponse {
|
||||||
|
fn from(value: SessionCreateResponse) -> Self {
|
||||||
|
let raw_message: RawSessionCreateResponseMsg = value.message.into();
|
||||||
|
Self {
|
||||||
|
message: serde_json::to_string(&raw_message).expect("infallible serializer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct SessionCreateResponseMsg {
|
||||||
|
nonce: Nonce,
|
||||||
|
quote: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionCreateResponseMsg {
|
||||||
|
pub fn into_tuple(self) -> (Nonce, Vec<u8>) {
|
||||||
|
(self.nonce, self.quote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RawSessionCreateResponseMsg {
|
||||||
|
nonce: HexBinary,
|
||||||
|
quote: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawSessionCreateResponseMsg> for SessionCreateResponseMsg {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawSessionCreateResponseMsg) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
nonce: value.nonce.to_array()?,
|
||||||
|
quote: value.quote.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SessionCreateResponseMsg> for RawSessionCreateResponseMsg {
|
||||||
|
fn from(value: SessionCreateResponseMsg) -> Self {
|
||||||
|
Self {
|
||||||
|
nonce: value.nonce.into(),
|
||||||
|
quote: value.quote.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct SessionSetPubKeyResponse {
|
||||||
|
message: SessionSetPubKeyResponseMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionSetPubKeyResponse {
|
||||||
|
pub fn new(nonce: Nonce, pub_key: VerifyingKey, quote: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
message: SessionSetPubKeyResponseMsg {
|
||||||
|
nonce,
|
||||||
|
pub_key,
|
||||||
|
quote,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quote(&self) -> &[u8] {
|
||||||
|
&self.message.quote
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_message(self) -> SessionSetPubKeyResponseMsg {
|
||||||
|
self.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawSessionSetPubKeyResponse> for SessionSetPubKeyResponse {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawSessionSetPubKeyResponse) -> Result<Self, Self::Error> {
|
||||||
|
let raw_message: RawSessionSetPubKeyResponseMsg = serde_json::from_str(&value.message)
|
||||||
|
.map_err(|e| StdError::parse_err("RawSessionSetPubKeyResponseMsg", e))?;
|
||||||
|
Ok(Self {
|
||||||
|
message: raw_message.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SessionSetPubKeyResponse> for RawSessionSetPubKeyResponse {
|
||||||
|
fn from(value: SessionSetPubKeyResponse) -> Self {
|
||||||
|
let raw_message: RawSessionSetPubKeyResponseMsg = value.message.into();
|
||||||
|
Self {
|
||||||
|
message: serde_json::to_string(&raw_message).expect("infallible serializer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct SessionSetPubKeyResponseMsg {
|
||||||
|
nonce: Nonce,
|
||||||
|
pub_key: VerifyingKey,
|
||||||
|
quote: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionSetPubKeyResponseMsg {
|
||||||
|
pub fn into_tuple(self) -> (VerifyingKey, Vec<u8>) {
|
||||||
|
(self.pub_key, self.quote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RawSessionSetPubKeyResponseMsg {
|
||||||
|
nonce: HexBinary,
|
||||||
|
pub_key: HexBinary,
|
||||||
|
quote: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawSessionSetPubKeyResponseMsg> for SessionSetPubKeyResponseMsg {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawSessionSetPubKeyResponseMsg) -> Result<Self, Self::Error> {
|
||||||
|
let pub_key = VerifyingKey::from_sec1_bytes(&value.pub_key)
|
||||||
|
.map_err(QuartzCwError::from)
|
||||||
|
.map_err(|e| StdError::generic_err(e.to_string()))?;
|
||||||
|
Ok(Self {
|
||||||
|
nonce: value.nonce.to_array()?,
|
||||||
|
pub_key,
|
||||||
|
quote: value.quote.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SessionSetPubKeyResponseMsg> for RawSessionSetPubKeyResponseMsg {
|
||||||
|
fn from(value: SessionSetPubKeyResponseMsg) -> Self {
|
||||||
|
Self {
|
||||||
|
nonce: value.nonce.into(),
|
||||||
|
pub_key: value.pub_key.to_sec1_bytes().into_vec().into(),
|
||||||
|
quote: value.quote.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue