-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
358 additions
and
308 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use anyhow::Error as AnyhowError; | ||
use cosmrs::AccountId; | ||
use quartz_common::enclave::{chain_client::ChainClient, handler::Handler}; | ||
use tendermint_rpc::event::Event as TmEvent; | ||
|
||
use crate::{ | ||
event::{query::QueryEvent, transfer::TransferEvent}, | ||
request::EnclaveRequest, | ||
}; | ||
|
||
pub mod query; | ||
pub mod transfer; | ||
|
||
#[derive(Clone, Debug)] | ||
pub enum EnclaveEvent { | ||
Transfer(TransferEvent), | ||
Query(QueryEvent), | ||
} | ||
|
||
impl TryFrom<TmEvent> for EnclaveEvent { | ||
type Error = (); | ||
|
||
fn try_from(value: TmEvent) -> Result<Self, Self::Error> { | ||
if let Ok(event) = TransferEvent::try_from(value.clone()) { | ||
Ok(Self::Transfer(event)) | ||
} else if let Ok(event) = QueryEvent::try_from(value) { | ||
Ok(Self::Query(event)) | ||
} else { | ||
Err(()) | ||
} | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl<C> Handler<C> for EnclaveEvent | ||
where | ||
C: ChainClient<Contract = AccountId>, | ||
{ | ||
type Error = AnyhowError; | ||
type Response = EnclaveRequest; | ||
|
||
async fn handle(self, ctx: &C) -> Result<Self::Response, Self::Error> { | ||
match self { | ||
EnclaveEvent::Transfer(event) => event.handle(ctx).await.map(EnclaveRequest::Update), | ||
EnclaveEvent::Query(event) => event.handle(ctx).await.map(EnclaveRequest::Query), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use anyhow::{anyhow, Error as AnyhowError}; | ||
use cosmrs::AccountId; | ||
use cosmwasm_std::{Addr, HexBinary}; | ||
use quartz_common::enclave::{chain_client::ChainClient, handler::Handler}; | ||
use serde_json::json; | ||
use tendermint_rpc::event::Event as TmEvent; | ||
use transfers_contract::msg::QueryMsg::GetState; | ||
|
||
use crate::{proto::QueryRequest, transfers_server::QueryRequestMessage}; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct QueryEvent { | ||
pub contract: AccountId, | ||
pub sender: String, | ||
pub ephemeral_pubkey: String, | ||
} | ||
|
||
impl TryFrom<TmEvent> for QueryEvent { | ||
type Error = AnyhowError; | ||
|
||
fn try_from(event: TmEvent) -> Result<Self, Self::Error> { | ||
let Some(events) = &event.events else { | ||
return Err(anyhow!("no events in tx")); | ||
}; | ||
|
||
if !events.keys().any(|k| k.starts_with("wasm-transfer.action")) { | ||
return Err(anyhow!("irrelevant event")); | ||
}; | ||
|
||
let contract = events | ||
.get("execute._contract_address") | ||
.ok_or_else(|| anyhow!("missing execute._contract_address in events"))? | ||
.first() | ||
.ok_or_else(|| anyhow!("execute._contract_address is empty"))? | ||
.parse::<AccountId>() | ||
.map_err(|e| anyhow!("failed to parse contract address: {}", e))?; | ||
|
||
let sender = events | ||
.get("message.sender") | ||
.ok_or_else(|| anyhow!("Missing message.sender in events"))? | ||
.first() | ||
.ok_or_else(|| anyhow!("execute.sender is empty"))? | ||
.to_owned(); | ||
|
||
let ephemeral_pubkey = events | ||
.get("wasm-query_balance.emphemeral_pubkey") | ||
.ok_or_else(|| anyhow!("Missing wasm-query_balance.emphemeral_pubkey in events"))? | ||
.first() | ||
.ok_or_else(|| anyhow!("execute.query_balance.emphemeral_pubkey is empty"))? | ||
.to_owned(); | ||
|
||
Ok(QueryEvent { | ||
contract, | ||
sender, | ||
ephemeral_pubkey, | ||
}) | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl<C> Handler<C> for QueryEvent | ||
where | ||
C: ChainClient<Contract = AccountId>, | ||
{ | ||
type Error = AnyhowError; | ||
type Response = QueryRequest; | ||
|
||
async fn handle(self, ctx: &C) -> Result<Self::Response, Self::Error> { | ||
let QueryEvent { | ||
contract, | ||
sender, | ||
ephemeral_pubkey, | ||
} = self; | ||
|
||
// Query contract state | ||
let state: HexBinary = ctx | ||
.query_contract(&contract, json!(GetState {}).to_string()) | ||
.await | ||
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?; | ||
|
||
// Build request | ||
let update_contents = QueryRequestMessage { | ||
state, | ||
address: Addr::unchecked(sender), // sender comes from TX event, therefore is checked | ||
ephemeral_pubkey: HexBinary::from_hex(&ephemeral_pubkey)?, | ||
}; | ||
|
||
// Send QueryRequestMessage to enclave over tonic gRPC client | ||
let request = QueryRequest { | ||
message: json!(update_contents).to_string(), | ||
}; | ||
|
||
Ok(request) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use anyhow::{anyhow, Error as AnyhowError}; | ||
use cosmrs::AccountId; | ||
use cosmwasm_std::{HexBinary, Uint64}; | ||
use quartz_common::{ | ||
contract::state::SEQUENCE_NUM_KEY, | ||
enclave::{chain_client::ChainClient, handler::Handler}, | ||
}; | ||
use serde_json::json; | ||
use tendermint_rpc::event::Event as TmEvent; | ||
use tracing::info; | ||
use transfers_contract::msg::{ | ||
execute::Request as TransferRequest, | ||
QueryMsg::{GetRequests, GetState}, | ||
}; | ||
|
||
use crate::{proto::UpdateRequest, transfers_server::UpdateRequestMessage}; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct TransferEvent { | ||
pub contract: AccountId, | ||
} | ||
|
||
impl TryFrom<TmEvent> for TransferEvent { | ||
type Error = AnyhowError; | ||
|
||
fn try_from(event: TmEvent) -> Result<Self, Self::Error> { | ||
let Some(events) = &event.events else { | ||
return Err(anyhow!("no events in tx")); | ||
}; | ||
|
||
if !events.keys().any(|k| k.starts_with("wasm-transfer.action")) { | ||
return Err(anyhow!("irrelevant event")); | ||
}; | ||
|
||
let contract = events | ||
.get("execute._contract_address") | ||
.ok_or_else(|| anyhow!("missing execute._contract_address in events"))? | ||
.first() | ||
.ok_or_else(|| anyhow!("execute._contract_address is empty"))? | ||
.parse::<AccountId>() | ||
.map_err(|e| anyhow!("failed to parse contract address: {}", e))?; | ||
|
||
Ok(TransferEvent { contract }) | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl<C> Handler<C> for TransferEvent | ||
where | ||
C: ChainClient<Contract = AccountId>, | ||
{ | ||
type Error = AnyhowError; | ||
type Response = UpdateRequest; | ||
|
||
async fn handle(self, ctx: &C) -> Result<Self::Response, Self::Error> { | ||
let contract = self.contract; | ||
|
||
// Query contract state | ||
let requests: Vec<TransferRequest> = ctx | ||
.query_contract(&contract, json!(GetRequests {}).to_string()) | ||
.await | ||
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?; | ||
|
||
let state: HexBinary = ctx | ||
.query_contract(&contract, json!(GetState {}).to_string()) | ||
.await | ||
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?; | ||
|
||
let seq_num: Uint64 = ctx | ||
.query_contract(&contract, SEQUENCE_NUM_KEY.to_string()) | ||
.await | ||
.map_err(|e| anyhow!("Problem querying contract state: {}", e))?; | ||
|
||
// Request body contents | ||
let update_contents = UpdateRequestMessage { | ||
state, | ||
requests, | ||
seq_num: seq_num.into(), | ||
}; | ||
|
||
// Wait 2 blocks | ||
info!("Waiting 2 blocks for light client proof"); | ||
ctx.wait_for_blocks(2) | ||
.await | ||
.map_err(|e| anyhow!("Problem waiting for proof: {}", e))?; | ||
|
||
// Call tm prover with trusted hash and height | ||
let proof = ctx | ||
.existence_proof(&contract, "requests") | ||
.await | ||
.map_err(|e| anyhow!("Problem getting existence proof: {}", e))?; | ||
|
||
// Merge the UpdateRequestMessage with the proof | ||
let mut proof_json = serde_json::to_value(proof)?; | ||
proof_json["msg"] = serde_json::to_value(&update_contents)?; | ||
|
||
// Build final request object | ||
let request = UpdateRequest { | ||
message: json!(proof_json).to_string(), | ||
}; | ||
|
||
Ok(request) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use cosmrs::AccountId; | ||
use k256::ecdsa::VerifyingKey; | ||
use quartz_common::enclave::{ | ||
attestor::Attestor, | ||
handler::Handler, | ||
key_manager::KeyManager, | ||
kv_store::{ConfigKey, ContractKey, NonceKey, TypedStore}, | ||
DefaultEnclave, | ||
}; | ||
use tonic::{Request, Response, Status}; | ||
|
||
use crate::proto::{ | ||
settlement_server::Settlement, QueryRequest, QueryResponse, UpdateRequest, UpdateResponse, | ||
}; | ||
|
||
#[tonic::async_trait] | ||
impl<A, K, S> Settlement for DefaultEnclave<A, K, S> | ||
where | ||
A: Attestor + Clone, | ||
K: KeyManager<PubKey = VerifyingKey> + Clone, | ||
S: TypedStore<ContractKey<AccountId>> + TypedStore<NonceKey> + TypedStore<ConfigKey> + Clone, | ||
{ | ||
async fn run( | ||
&self, | ||
request: Request<UpdateRequest>, | ||
) -> Result<Response<UpdateResponse>, Status> { | ||
request.handle(self).await | ||
} | ||
|
||
async fn query( | ||
&self, | ||
request: Request<QueryRequest>, | ||
) -> Result<Response<QueryResponse>, Status> { | ||
request.handle(self).await | ||
} | ||
} |
Oops, something went wrong.