Transfers app: query impl (#96)
Co-authored-by: Daniel Gushchyan <d.gushchyan@gmail.com> Co-authored-by: Ethan Buchman <ethan@coinculture.info> Co-authored-by: David Kajpust <kajpustd@gmail.com> Co-authored-by: Daniel Gushchyan <39884512+dangush@users.noreply.github.com> Co-authored-by: David Kajpust <davidkajpust@davids-mbp.home> Co-authored-by: dave <davidkajpust@informal.systems>
This commit is contained in:
parent
0335838949
commit
365fcd1115
14 changed files with 1019 additions and 351 deletions
611
Cargo.lock
generated
611
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -185,7 +185,9 @@ bash scripts/build.sh
|
||||||
### Configure and Run Gramine
|
### Configure and Run Gramine
|
||||||
|
|
||||||
Setup and sign the Gramine config, and then start the gramine process, which will run the
|
Setup and sign the Gramine config, and then start the gramine process, which will run the
|
||||||
grpc server that hosts the transfer application.
|
grpc server that hosts the transfer application. The quartz port defaults to `11090`, but you can set it deliberately with
|
||||||
|
`export QUARTZ_PORT=XXXXX`. It is best to set it everytime, since if 2 people are sshing into the same machine to use
|
||||||
|
the secure enclave, this could create undesired env conditions where your app is talking to the wrong enclave.
|
||||||
|
|
||||||
```
|
```
|
||||||
bash scripts/start.sh
|
bash scripts/start.sh
|
||||||
|
|
1
apps/transfers/contracts/Cargo.lock
generated
1
apps/transfers/contracts/Cargo.lock
generated
|
@ -1250,6 +1250,7 @@ dependencies = [
|
||||||
"cw20-base",
|
"cw20-base",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"quartz-cw",
|
"quartz-cw",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
@ -30,6 +30,7 @@ mock-sgx = ["quartz-cw/mock-sgx"]
|
||||||
# external
|
# external
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
|
serde = { version = "1.0.137", default-features = false, features = ["derive"] }
|
||||||
thiserror = { version = "1.0.49" }
|
thiserror = { version = "1.0.49" }
|
||||||
|
|
||||||
# cosmwasm
|
# cosmwasm
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
use cosmwasm_std::{entry_point, DepsMut, Env, HexBinary, MessageInfo, Response};
|
use cosmwasm_std::{
|
||||||
|
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, HexBinary, MessageInfo, Response,
|
||||||
|
StdResult,
|
||||||
|
};
|
||||||
use quartz_cw::handler::RawHandler;
|
use quartz_cw::handler::RawHandler;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::ContractError,
|
error::ContractError,
|
||||||
msg::{
|
msg::{
|
||||||
execute::{Request, UpdateMsg},
|
execute::{QueryResponseMsg, Request, UpdateMsg},
|
||||||
ExecuteMsg, InstantiateMsg,
|
ExecuteMsg, InstantiateMsg, QueryMsg,
|
||||||
},
|
},
|
||||||
state::{DENOM, REQUESTS, STATE},
|
state::{BALANCES, DENOM, REQUESTS, STATE},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
|
@ -43,19 +46,58 @@ pub fn execute(
|
||||||
use execute::*;
|
use execute::*;
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
|
// Quartz msgs
|
||||||
ExecuteMsg::Quartz(msg) => msg.handle_raw(deps, &env, &info).map_err(Into::into),
|
ExecuteMsg::Quartz(msg) => msg.handle_raw(deps, &env, &info).map_err(Into::into),
|
||||||
|
|
||||||
|
// Clear user msgs
|
||||||
|
ExecuteMsg::Deposit => deposit(deps, env, info),
|
||||||
|
ExecuteMsg::Withdraw => withdraw(deps, env, info),
|
||||||
|
ExecuteMsg::ClearTextTransferRequest(_) => unimplemented!(),
|
||||||
|
ExecuteMsg::QueryRequest(msg) => query_balance(deps, env, info, msg),
|
||||||
|
|
||||||
|
// Cipher user msgs
|
||||||
ExecuteMsg::TransferRequest(msg) => transfer_request(deps, env, info, msg),
|
ExecuteMsg::TransferRequest(msg) => transfer_request(deps, env, info, msg),
|
||||||
|
|
||||||
|
// Enclave msgs
|
||||||
ExecuteMsg::Update(attested_msg) => {
|
ExecuteMsg::Update(attested_msg) => {
|
||||||
let _ = attested_msg
|
let _ = attested_msg
|
||||||
.clone()
|
.clone()
|
||||||
.handle_raw(deps.branch(), &env, &info)?;
|
.handle_raw(deps.branch(), &env, &info)?;
|
||||||
|
let UpdateMsg {
|
||||||
// Extract underlying UpdateMsg and pass to update()
|
ciphertext,
|
||||||
update(deps, env, info, UpdateMsg(attested_msg.msg))
|
quantity,
|
||||||
|
withdrawals,
|
||||||
|
} = attested_msg.msg.0;
|
||||||
|
update(
|
||||||
|
deps,
|
||||||
|
env,
|
||||||
|
info,
|
||||||
|
UpdateMsg {
|
||||||
|
ciphertext,
|
||||||
|
quantity,
|
||||||
|
withdrawals,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteMsg::QueryResponse(attested_msg) => {
|
||||||
|
let _ = attested_msg
|
||||||
|
.clone()
|
||||||
|
.handle_raw(deps.branch(), &env, &info)?;
|
||||||
|
let QueryResponseMsg {
|
||||||
|
address,
|
||||||
|
encrypted_bal,
|
||||||
|
} = attested_msg.msg.0;
|
||||||
|
store_balance(
|
||||||
|
deps,
|
||||||
|
env,
|
||||||
|
info,
|
||||||
|
QueryResponseMsg {
|
||||||
|
address,
|
||||||
|
encrypted_bal,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ExecuteMsg::Deposit => deposit(deps, env, info),
|
|
||||||
ExecuteMsg::Withdraw => withdraw(deps, env, info),
|
|
||||||
ExecuteMsg::ClearTextTransferRequest(_) => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,10 +107,56 @@ pub mod execute {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::ContractError,
|
error::ContractError,
|
||||||
msg::execute::{Request, TransferRequestMsg, UpdateMsg},
|
msg::execute::{QueryRequestMsg, QueryResponseMsg, Request, TransferRequestMsg, UpdateMsg},
|
||||||
state::{DENOM, REQUESTS, STATE},
|
state::{BALANCES, DENOM, REQUESTS, STATE},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn deposit(deps: DepsMut, _env: Env, info: MessageInfo) -> Result<Response, ContractError> {
|
||||||
|
let denom: String = DENOM.load(deps.storage)?;
|
||||||
|
let quantity = must_pay(&info, &denom)?;
|
||||||
|
|
||||||
|
let mut requests = REQUESTS.load(deps.storage)?;
|
||||||
|
|
||||||
|
requests.push(Request::Deposit(info.sender, quantity));
|
||||||
|
|
||||||
|
REQUESTS.save(deps.storage, &requests)?;
|
||||||
|
|
||||||
|
let event = Event::new("transfer").add_attribute("action", "user");
|
||||||
|
let resp = Response::new().add_event(event);
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn withdraw(
|
||||||
|
deps: DepsMut,
|
||||||
|
_env: Env,
|
||||||
|
info: MessageInfo,
|
||||||
|
) -> Result<Response, ContractError> {
|
||||||
|
let mut requests = REQUESTS.load(deps.storage)?;
|
||||||
|
|
||||||
|
requests.push(Request::Withdraw(info.sender));
|
||||||
|
|
||||||
|
REQUESTS.save(deps.storage, &requests)?;
|
||||||
|
|
||||||
|
let event = Event::new("transfer").add_attribute("action", "user");
|
||||||
|
let resp = Response::new().add_event(event);
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query_balance(
|
||||||
|
_deps: DepsMut,
|
||||||
|
_env: Env,
|
||||||
|
_info: MessageInfo,
|
||||||
|
msg: QueryRequestMsg,
|
||||||
|
) -> Result<Response, ContractError> {
|
||||||
|
let event = Event::new("query_balance")
|
||||||
|
.add_attribute("query", "user")
|
||||||
|
.add_attribute("emphemeral_pubkey", msg.emphemeral_pubkey.to_string());
|
||||||
|
let resp = Response::new().add_event(event);
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transfer_request(
|
pub fn transfer_request(
|
||||||
deps: DepsMut,
|
deps: DepsMut,
|
||||||
_env: Env,
|
_env: Env,
|
||||||
|
@ -94,12 +182,12 @@ pub mod execute {
|
||||||
msg: UpdateMsg,
|
msg: UpdateMsg,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
// Store state
|
// Store state
|
||||||
STATE.save(deps.storage, &msg.0.ciphertext)?;
|
STATE.save(deps.storage, &msg.ciphertext)?;
|
||||||
|
|
||||||
// Clear queue
|
// Clear queue
|
||||||
let mut requests: Vec<Request> = REQUESTS.load(deps.storage)?;
|
let mut requests: Vec<Request> = REQUESTS.load(deps.storage)?;
|
||||||
|
|
||||||
requests.drain(0..msg.0.quantity as usize);
|
requests.drain(0..msg.quantity as usize);
|
||||||
|
|
||||||
REQUESTS.save(deps.storage, &requests)?;
|
REQUESTS.save(deps.storage, &requests)?;
|
||||||
|
|
||||||
|
@ -107,7 +195,6 @@ pub mod execute {
|
||||||
let denom = DENOM.load(deps.storage)?;
|
let denom = DENOM.load(deps.storage)?;
|
||||||
|
|
||||||
let messages = msg
|
let messages = msg
|
||||||
.0
|
|
||||||
.withdrawals
|
.withdrawals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(user, funds)| BankMsg::Send {
|
.map(|(user, funds)| BankMsg::Send {
|
||||||
|
@ -120,38 +207,36 @@ pub mod execute {
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deposit(deps: DepsMut, _env: Env, info: MessageInfo) -> Result<Response, ContractError> {
|
pub fn store_balance(
|
||||||
let denom = DENOM.load(deps.storage)?;
|
|
||||||
let quantity = must_pay(&info, &denom)?;
|
|
||||||
|
|
||||||
let mut requests = REQUESTS.load(deps.storage)?;
|
|
||||||
|
|
||||||
requests.push(Request::Deposit(info.sender, quantity));
|
|
||||||
|
|
||||||
REQUESTS.save(deps.storage, &requests)?;
|
|
||||||
|
|
||||||
let event = Event::new("transfer").add_attribute("action", "user");
|
|
||||||
let resp = Response::new().add_event(event);
|
|
||||||
|
|
||||||
Ok(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn withdraw(
|
|
||||||
deps: DepsMut,
|
deps: DepsMut,
|
||||||
_env: Env,
|
_env: Env,
|
||||||
info: MessageInfo,
|
_info: MessageInfo,
|
||||||
|
msg: QueryResponseMsg,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
// TODO: verify denom
|
// Store state
|
||||||
|
BALANCES.save(deps.storage, &msg.address.to_string(), &msg.encrypted_bal)?;
|
||||||
|
|
||||||
let mut requests = REQUESTS.load(deps.storage)?;
|
// Emit event
|
||||||
|
let event = Event::new("store_balance")
|
||||||
requests.push(Request::Withdraw(info.sender));
|
.add_attribute("query", "enclave") // TODO Weird to name it enclave?
|
||||||
|
.add_attribute("address", msg.address.to_string())
|
||||||
REQUESTS.save(deps.storage, &requests)?;
|
.add_attribute("encrypted_balance", msg.encrypted_bal.to_string());
|
||||||
|
|
||||||
let event = Event::new("transfer").add_attribute("action", "user");
|
|
||||||
let resp = Response::new().add_event(event);
|
let resp = Response::new().add_event(event);
|
||||||
|
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
|
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
||||||
|
match msg {
|
||||||
|
QueryMsg::GetBalance { address } => to_json_binary(&query::get_balance(deps, address)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod query {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn get_balance(deps: Deps, address: String) -> StdResult<HexBinary> {
|
||||||
|
let balance = BALANCES.may_load(deps.storage, &address)?;
|
||||||
|
Ok(balance.unwrap_or_default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
|
||||||
use quartz_cw::{
|
use quartz_cw::{
|
||||||
msg::execute::attested::{RawAttested, RawDefaultAttestation},
|
msg::execute::attested::{RawAttested, RawAttestedMsgSansHandler, RawDefaultAttestation},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
type AttestedMsg<M, RA = RawDefaultAttestation> = RawAttested<RawAttestedMsgSansHandler<M>, RA>;
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct InstantiateMsg<RA = RawDefaultAttestation> {
|
pub struct InstantiateMsg<RA = RawDefaultAttestation> {
|
||||||
|
@ -11,45 +13,37 @@ pub struct InstantiateMsg<RA = RawDefaultAttestation> {
|
||||||
pub denom: String,
|
pub denom: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub enum QueryMsg {
|
||||||
|
GetBalance { address: String },
|
||||||
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum ExecuteMsg<RA = RawDefaultAttestation> {
|
pub enum ExecuteMsg<RA = RawDefaultAttestation> {
|
||||||
// quartz initialization
|
// quartz initialization
|
||||||
Quartz(QuartzExecuteMsg),
|
Quartz(QuartzExecuteMsg),
|
||||||
|
|
||||||
// ----- user txs
|
// User msgs
|
||||||
// clear text
|
// clear text
|
||||||
Deposit,
|
Deposit,
|
||||||
Withdraw,
|
Withdraw,
|
||||||
|
ClearTextTransferRequest(execute::ClearTextTransferRequestMsg),
|
||||||
// ciphertext
|
// ciphertext
|
||||||
TransferRequest(execute::TransferRequestMsg),
|
TransferRequest(execute::TransferRequestMsg),
|
||||||
// ---- end user txs
|
QueryRequest(execute::QueryRequestMsg),
|
||||||
ClearTextTransferRequest(execute::ClearTextTransferRequestMsg),
|
|
||||||
|
|
||||||
// enclave msg
|
// Enclave msgs
|
||||||
Update(RawAttested<execute::RawUpdateMsg, RA>),
|
Update(AttestedMsg<execute::UpdateMsg, RA>),
|
||||||
|
QueryResponse(AttestedMsg<execute::QueryResponseMsg, RA>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod execute {
|
pub mod execute {
|
||||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError};
|
use cosmwasm_schema::cw_serde;
|
||||||
use quartz_cw::{
|
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
||||||
error::Error,
|
use quartz_cw::{msg::execute::attested::HasUserData, state::UserData};
|
||||||
handler::Handler,
|
|
||||||
msg::{execute::attested::HasUserData, HasDomainType},
|
|
||||||
state::UserData,
|
|
||||||
};
|
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[cw_serde]
|
|
||||||
pub struct TransferRequestMsg {
|
|
||||||
pub ciphertext: HexBinary,
|
|
||||||
pub digest: HexBinary,
|
|
||||||
// pub proof: π
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct ClearTextTransferRequestMsg {
|
pub struct ClearTextTransferRequestMsg {
|
||||||
pub sender: Addr,
|
pub sender: Addr,
|
||||||
|
@ -58,7 +52,18 @@ pub mod execute {
|
||||||
// pub proof: π
|
// pub proof: π
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ciphertext of a transfer request
|
#[cw_serde]
|
||||||
|
pub struct QueryRequestMsg {
|
||||||
|
pub emphemeral_pubkey: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct TransferRequestMsg {
|
||||||
|
pub ciphertext: HexBinary,
|
||||||
|
pub digest: HexBinary,
|
||||||
|
// pub proof: π
|
||||||
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
Transfer(HexBinary),
|
Transfer(HexBinary),
|
||||||
|
@ -67,20 +72,17 @@ pub mod execute {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct RawUpdateMsg {
|
pub struct UpdateMsg {
|
||||||
pub ciphertext: HexBinary,
|
pub ciphertext: HexBinary,
|
||||||
pub quantity: u32,
|
pub quantity: u32,
|
||||||
pub withdrawals: Vec<(Addr, Uint128)>,
|
pub withdrawals: Vec<(Addr, Uint128)>,
|
||||||
// pub proof: π
|
// pub proof: π
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct UpdateMsg(pub RawUpdateMsg);
|
|
||||||
|
|
||||||
impl HasUserData for UpdateMsg {
|
impl HasUserData for UpdateMsg {
|
||||||
fn user_data(&self) -> UserData {
|
fn user_data(&self) -> UserData {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(serde_json::to_string(&self.0).expect("infallible serializer"));
|
hasher.update(serde_json::to_string(&self).expect("infallible serializer"));
|
||||||
let digest: [u8; 32] = hasher.finalize().into();
|
let digest: [u8; 32] = hasher.finalize().into();
|
||||||
|
|
||||||
let mut user_data = [0u8; 64];
|
let mut user_data = [0u8; 64];
|
||||||
|
@ -89,33 +91,22 @@ pub mod execute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasDomainType for RawUpdateMsg {
|
#[cw_serde]
|
||||||
type DomainType = UpdateMsg;
|
pub struct QueryResponseMsg {
|
||||||
|
pub address: Addr,
|
||||||
|
pub encrypted_bal: HexBinary,
|
||||||
|
// pub proof: π
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RawUpdateMsg> for UpdateMsg {
|
impl HasUserData for QueryResponseMsg {
|
||||||
type Error = StdError;
|
fn user_data(&self) -> UserData {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(serde_json::to_string(&self).expect("infallible serializer"));
|
||||||
|
let digest: [u8; 32] = hasher.finalize().into();
|
||||||
|
|
||||||
fn try_from(value: RawUpdateMsg) -> Result<Self, Self::Error> {
|
let mut user_data = [0u8; 64];
|
||||||
Ok(Self(value))
|
user_data[0..32].copy_from_slice(&digest);
|
||||||
}
|
user_data
|
||||||
}
|
|
||||||
|
|
||||||
impl From<UpdateMsg> for RawUpdateMsg {
|
|
||||||
fn from(value: UpdateMsg) -> Self {
|
|
||||||
value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler for UpdateMsg {
|
|
||||||
fn handle(
|
|
||||||
self,
|
|
||||||
_deps: DepsMut<'_>,
|
|
||||||
_env: &Env,
|
|
||||||
_info: &MessageInfo,
|
|
||||||
) -> Result<Response, Error> {
|
|
||||||
// basically handle `transfer_request` here
|
|
||||||
Ok(Response::default())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use cosmwasm_std::HexBinary;
|
use cosmwasm_std::HexBinary;
|
||||||
use cw_storage_plus::Item;
|
use cw_storage_plus::{Item, Map};
|
||||||
|
|
||||||
use crate::msg::execute::Request;
|
use crate::msg::execute::Request;
|
||||||
|
|
||||||
pub const STATE: Item<HexBinary> = Item::new("state");
|
pub const STATE: Item<HexBinary> = Item::new("state");
|
||||||
pub const REQUESTS: Item<Vec<Request>> = Item::new("requests");
|
pub const REQUESTS: Item<Vec<Request>> = Item::new("requests");
|
||||||
|
|
||||||
pub const DENOM: Item<String> = Item::new("donation_denom");
|
pub const DENOM: Item<String> = Item::new("donation_denom");
|
||||||
|
pub const BALANCES: Map<&str, HexBinary> = Map::new("balances");
|
||||||
|
|
|
@ -3,13 +3,22 @@ syntax = "proto3";
|
||||||
package transfers;
|
package transfers;
|
||||||
|
|
||||||
service Settlement {
|
service Settlement {
|
||||||
rpc Run (RunTransfersRequest) returns (RunTransfersResponse) {}
|
rpc Run (UpdateRequest) returns (UpdateResponse) {}
|
||||||
|
rpc Query (QueryRequest) returns (QueryResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message RunTransfersRequest {
|
message UpdateRequest {
|
||||||
string message = 1;
|
string message = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RunTransfersResponse {
|
message UpdateResponse {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message QueryRequest {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message QueryResponse {
|
||||||
string message = 1;
|
string message = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
// This file is @generated by prost-build.
|
// This file is @generated by prost-build.
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct RunTransfersRequest {
|
pub struct UpdateRequest {
|
||||||
#[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)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct RunTransfersResponse {
|
pub struct UpdateResponse {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub message: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct QueryRequest {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub message: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct QueryResponse {
|
||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub message: ::prost::alloc::string::String,
|
pub message: ::prost::alloc::string::String,
|
||||||
}
|
}
|
||||||
|
@ -98,11 +110,8 @@ pub mod settlement_client {
|
||||||
}
|
}
|
||||||
pub async fn run(
|
pub async fn run(
|
||||||
&mut self,
|
&mut self,
|
||||||
request: impl tonic::IntoRequest<super::RunTransfersRequest>,
|
request: impl tonic::IntoRequest<super::UpdateRequest>,
|
||||||
) -> std::result::Result<
|
) -> std::result::Result<tonic::Response<super::UpdateResponse>, tonic::Status> {
|
||||||
tonic::Response<super::RunTransfersResponse>,
|
|
||||||
tonic::Status,
|
|
||||||
> {
|
|
||||||
self.inner
|
self.inner
|
||||||
.ready()
|
.ready()
|
||||||
.await
|
.await
|
||||||
|
@ -118,6 +127,28 @@ pub mod settlement_client {
|
||||||
req.extensions_mut().insert(GrpcMethod::new("transfers.Settlement", "Run"));
|
req.extensions_mut().insert(GrpcMethod::new("transfers.Settlement", "Run"));
|
||||||
self.inner.unary(req, path, codec).await
|
self.inner.unary(req, path, codec).await
|
||||||
}
|
}
|
||||||
|
pub async fn query(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::QueryRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::QueryResponse>, 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.Settlement/Query",
|
||||||
|
);
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut()
|
||||||
|
.insert(GrpcMethod::new("transfers.Settlement", "Query"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Generated server implementations.
|
/// Generated server implementations.
|
||||||
|
@ -129,11 +160,12 @@ pub mod settlement_server {
|
||||||
pub trait Settlement: Send + Sync + 'static {
|
pub trait Settlement: Send + Sync + 'static {
|
||||||
async fn run(
|
async fn run(
|
||||||
&self,
|
&self,
|
||||||
request: tonic::Request<super::RunTransfersRequest>,
|
request: tonic::Request<super::UpdateRequest>,
|
||||||
) -> std::result::Result<
|
) -> std::result::Result<tonic::Response<super::UpdateResponse>, tonic::Status>;
|
||||||
tonic::Response<super::RunTransfersResponse>,
|
async fn query(
|
||||||
tonic::Status,
|
&self,
|
||||||
>;
|
request: tonic::Request<super::QueryRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::QueryResponse>, tonic::Status>;
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SettlementServer<T: Settlement> {
|
pub struct SettlementServer<T: Settlement> {
|
||||||
|
@ -217,18 +249,16 @@ pub mod settlement_server {
|
||||||
"/transfers.Settlement/Run" => {
|
"/transfers.Settlement/Run" => {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct RunSvc<T: Settlement>(pub Arc<T>);
|
struct RunSvc<T: Settlement>(pub Arc<T>);
|
||||||
impl<
|
impl<T: Settlement> tonic::server::UnaryService<super::UpdateRequest>
|
||||||
T: Settlement,
|
|
||||||
> tonic::server::UnaryService<super::RunTransfersRequest>
|
|
||||||
for RunSvc<T> {
|
for RunSvc<T> {
|
||||||
type Response = super::RunTransfersResponse;
|
type Response = super::UpdateResponse;
|
||||||
type Future = BoxFuture<
|
type Future = BoxFuture<
|
||||||
tonic::Response<Self::Response>,
|
tonic::Response<Self::Response>,
|
||||||
tonic::Status,
|
tonic::Status,
|
||||||
>;
|
>;
|
||||||
fn call(
|
fn call(
|
||||||
&mut self,
|
&mut self,
|
||||||
request: tonic::Request<super::RunTransfersRequest>,
|
request: tonic::Request<super::UpdateRequest>,
|
||||||
) -> Self::Future {
|
) -> Self::Future {
|
||||||
let inner = Arc::clone(&self.0);
|
let inner = Arc::clone(&self.0);
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
|
@ -260,6 +290,50 @@ pub mod settlement_server {
|
||||||
};
|
};
|
||||||
Box::pin(fut)
|
Box::pin(fut)
|
||||||
}
|
}
|
||||||
|
"/transfers.Settlement/Query" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct QuerySvc<T: Settlement>(pub Arc<T>);
|
||||||
|
impl<T: Settlement> tonic::server::UnaryService<super::QueryRequest>
|
||||||
|
for QuerySvc<T> {
|
||||||
|
type Response = super::QueryResponse;
|
||||||
|
type Future = BoxFuture<
|
||||||
|
tonic::Response<Self::Response>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::QueryRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move {
|
||||||
|
<T as Settlement>::query(&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 = QuerySvc(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 {
|
Box::pin(async move {
|
||||||
Ok(
|
Ok(
|
||||||
|
|
|
@ -32,3 +32,27 @@ impl TryFrom<RawState> for State {
|
||||||
Ok(Self { state: o.state })
|
Ok(Self { state: o.state })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Balance {
|
||||||
|
pub balance: Uint128,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub struct RawBalance {
|
||||||
|
pub balance: Uint128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Balance> for RawBalance {
|
||||||
|
fn from(o: Balance) -> Self {
|
||||||
|
Self { balance: o.balance }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawBalance> for Balance {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(o: RawBalance) -> Result<Self, anyhow::Error> {
|
||||||
|
Ok(Self { balance: o.balance })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,10 @@ use tonic::{Request, Response, Result as TonicResult, Status};
|
||||||
use transfers_contract::msg::execute::{ClearTextTransferRequestMsg, Request as TransfersRequest};
|
use transfers_contract::msg::execute::{ClearTextTransferRequestMsg, Request as TransfersRequest};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
proto::{settlement_server::Settlement, RunTransfersRequest, RunTransfersResponse},
|
proto::{
|
||||||
state::{RawState, State},
|
settlement_server::Settlement, QueryRequest, QueryResponse, UpdateRequest, UpdateResponse,
|
||||||
|
},
|
||||||
|
state::{RawBalance, RawState, State},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type RawCipherText = HexBinary;
|
pub type RawCipherText = HexBinary;
|
||||||
|
@ -30,19 +32,32 @@ pub struct TransfersService<A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct RunTransfersRequestMessage {
|
pub struct UpdateRequestMessage {
|
||||||
state: HexBinary,
|
state: HexBinary,
|
||||||
requests: Vec<TransfersRequest>,
|
requests: Vec<TransfersRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct RunTransfersResponseMessage {
|
pub struct QueryRequestMessage {
|
||||||
|
state: HexBinary,
|
||||||
|
address: Addr,
|
||||||
|
ephemeral_pubkey: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct QueryResponseMessage {
|
||||||
|
address: Addr,
|
||||||
|
encrypted_bal: HexBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct UpdateMsg {
|
||||||
ciphertext: HexBinary,
|
ciphertext: HexBinary,
|
||||||
quantity: u32,
|
quantity: u32,
|
||||||
withdrawals: Vec<(Addr, Uint128)>,
|
withdrawals: Vec<(Addr, Uint128)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasUserData for RunTransfersResponseMessage {
|
impl HasUserData for UpdateMsg {
|
||||||
fn user_data(&self) -> UserData {
|
fn user_data(&self) -> UserData {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(serde_json::to_string(&self).expect("infallible serializer"));
|
hasher.update(serde_json::to_string(&self).expect("infallible serializer"));
|
||||||
|
@ -54,6 +69,24 @@ impl HasUserData for RunTransfersResponseMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasUserData for QueryResponseMessage {
|
||||||
|
fn user_data(&self) -> UserData {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(serde_json::to_string(&self).expect("infallible serializer"));
|
||||||
|
let digest: [u8; 32] = hasher.finalize().into();
|
||||||
|
|
||||||
|
let mut user_data = [0u8; 64];
|
||||||
|
user_data[0..32].copy_from_slice(&digest);
|
||||||
|
user_data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
struct AttestedMsg<M> {
|
||||||
|
msg: M,
|
||||||
|
quote: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<A> TransfersService<A>
|
impl<A> TransfersService<A>
|
||||||
where
|
where
|
||||||
A: Attestor,
|
A: Attestor,
|
||||||
|
@ -68,14 +101,11 @@ impl<A> Settlement for TransfersService<A>
|
||||||
where
|
where
|
||||||
A: Attestor + Send + Sync + 'static,
|
A: Attestor + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
async fn run(
|
async fn run(&self, request: Request<UpdateRequest>) -> TonicResult<Response<UpdateResponse>> {
|
||||||
&self,
|
|
||||||
request: Request<RunTransfersRequest>,
|
|
||||||
) -> TonicResult<Response<RunTransfersResponse>> {
|
|
||||||
// Request contains a serialized json string
|
// Request contains a serialized json string
|
||||||
|
|
||||||
// Serialize request into struct containing State and the Requests vec
|
// Serialize request into struct containing State and the Requests vec
|
||||||
let message: RunTransfersRequestMessage = {
|
let message: UpdateRequestMessage = {
|
||||||
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()))?
|
||||||
};
|
};
|
||||||
|
@ -83,8 +113,6 @@ where
|
||||||
// Decrypt and deserialize the state
|
// Decrypt and deserialize the state
|
||||||
let mut state = {
|
let mut state = {
|
||||||
if message.state.len() == 1 && message.state[0] == 0 {
|
if message.state.len() == 1 && message.state[0] == 0 {
|
||||||
println!("{}", message.state);
|
|
||||||
|
|
||||||
State {
|
State {
|
||||||
state: BTreeMap::<Addr, Uint128>::new(),
|
state: BTreeMap::<Addr, Uint128>::new(),
|
||||||
}
|
}
|
||||||
|
@ -103,7 +131,7 @@ where
|
||||||
|
|
||||||
let requests_len = message.requests.len() as u32;
|
let requests_len = message.requests.len() as u32;
|
||||||
// Instantiate empty withdrawals map to include in response (Update message to smart contract)
|
// Instantiate empty withdrawals map to include in response (Update message to smart contract)
|
||||||
let mut withdrawals_response = Vec::<(Addr, Uint128)>::new();
|
let mut withdrawals_response: Vec<(Addr, Uint128)> = Vec::<(Addr, Uint128)>::new();
|
||||||
|
|
||||||
// Loop through requests, match on cases, and apply changes to state
|
// Loop through requests, match on cases, and apply changes to state
|
||||||
for req in message.requests {
|
for req in message.requests {
|
||||||
|
@ -170,7 +198,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare message to chain
|
// Prepare message to chain
|
||||||
let msg = RunTransfersResponseMessage {
|
let msg = UpdateMsg {
|
||||||
ciphertext: state_enc,
|
ciphertext: state_enc,
|
||||||
quantity: requests_len,
|
quantity: requests_len,
|
||||||
withdrawals: withdrawals_response,
|
withdrawals: withdrawals_response,
|
||||||
|
@ -187,7 +215,69 @@ where
|
||||||
let message =
|
let message =
|
||||||
serde_json::to_string(&attested_msg).map_err(|e| Status::internal(e.to_string()))?;
|
serde_json::to_string(&attested_msg).map_err(|e| Status::internal(e.to_string()))?;
|
||||||
|
|
||||||
Ok(Response::new(RunTransfersResponse { message }))
|
Ok(Response::new(UpdateResponse { message }))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn query(&self, request: Request<QueryRequest>) -> TonicResult<Response<QueryResponse>> {
|
||||||
|
// Serialize request into struct containing State and the Requests vec
|
||||||
|
let message: QueryRequestMessage = {
|
||||||
|
let message: String = request.into_inner().message;
|
||||||
|
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decrypt and deserialize the state
|
||||||
|
let state = {
|
||||||
|
if message.state.len() == 1 && message.state[0] == 0 {
|
||||||
|
State {
|
||||||
|
state: BTreeMap::<Addr, Uint128>::new(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let sk_lock = self
|
||||||
|
.sk
|
||||||
|
.lock()
|
||||||
|
.map_err(|e| Status::internal(e.to_string()))?;
|
||||||
|
let sk = sk_lock
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(Status::internal("SigningKey unavailable"))?;
|
||||||
|
decrypt_state(sk, &message.state)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let bal = match state.state.get(&message.address) {
|
||||||
|
Some(balance) => RawBalance { balance: *balance },
|
||||||
|
None => RawBalance {
|
||||||
|
balance: Uint128::new(0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse the ephemeral public key
|
||||||
|
let ephemeral_pubkey =
|
||||||
|
VerifyingKey::from_sec1_bytes(&message.ephemeral_pubkey).map_err(|e| {
|
||||||
|
Status::invalid_argument(format!("Invalid ephemeral public key: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Encrypt the balance using the ephemeral public key
|
||||||
|
let bal_enc = encrypt_balance(bal, ephemeral_pubkey)
|
||||||
|
.map_err(|e| Status::internal(format!("Encryption error: {}", e)))?;
|
||||||
|
|
||||||
|
// Prepare message to chain
|
||||||
|
let msg = QueryResponseMessage {
|
||||||
|
address: message.address,
|
||||||
|
encrypted_bal: bal_enc,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attest to message
|
||||||
|
let attestation: HexBinary = self
|
||||||
|
.attestor
|
||||||
|
.quote(msg.clone())
|
||||||
|
.map_err(|e| Status::internal(e.to_string()))?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let attested_msg = RawAttested { msg, attestation };
|
||||||
|
let message =
|
||||||
|
serde_json::to_string(&attested_msg).map_err(|e| Status::internal(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(Response::new(QueryResponse { message }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,3 +311,12 @@ fn encrypt_state(state: RawState, enclave_pk: VerifyingKey) -> TonicResult<RawCi
|
||||||
Err(e) => Err(Status::internal(format!("Encryption error: {}", e))),
|
Err(e) => Err(Status::internal(format!("Encryption error: {}", e))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encrypt_balance(balance: RawBalance, ephemeral_pk: VerifyingKey) -> TonicResult<RawCipherText> {
|
||||||
|
let serialized_balance = serde_json::to_string(&balance).expect("infallible serializer");
|
||||||
|
|
||||||
|
match encrypt(&ephemeral_pk.to_sec1_bytes(), serialized_balance.as_bytes()) {
|
||||||
|
Ok(encrypted_balance) => Ok(encrypted_balance.into()),
|
||||||
|
Err(e) => Err(Status::internal(format!("Encryption error: {}", e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
50
apps/transfers/scripts/decrypt_ephemeral.sh
Executable file
50
apps/transfers/scripts/decrypt_ephemeral.sh
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if an encrypted balance is provided
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
echo "Usage: $0 <encrypted_balance_hex>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENCRYPTED_BALANCE=$1
|
||||||
|
|
||||||
|
# Check if eciespy is installed
|
||||||
|
if ! pip list 2>/dev/null | grep -q eciespy; then
|
||||||
|
echo "eciespy is not installed. Installing now..."
|
||||||
|
pip install eciespy >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the private key from wasmd
|
||||||
|
EPHEMERAL_PRIVKEY=$( yes | wasmd keys export ephemeral_user --unsafe --unarmored-hex)
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to export private key. Make sure 'ephemeral_user' exists and you've entered the correct password."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a temporary Python script for decryption
|
||||||
|
TEMP_PYTHON_SCRIPT=$(mktemp)
|
||||||
|
cat << EOF > "$TEMP_PYTHON_SCRIPT"
|
||||||
|
from ecies import decrypt
|
||||||
|
import binascii
|
||||||
|
import sys
|
||||||
|
|
||||||
|
private_key = bytes.fromhex(sys.argv[1])
|
||||||
|
encrypted = binascii.unhexlify(sys.argv[2])
|
||||||
|
|
||||||
|
try:
|
||||||
|
decrypted = decrypt(private_key, encrypted)
|
||||||
|
print(decrypted.decode())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Decryption failed: {str(e)}")
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Run the Python script to decrypt
|
||||||
|
DECRYPTED=$(python3 "$TEMP_PYTHON_SCRIPT" "$EPHEMERAL_PRIVKEY" "$ENCRYPTED_BALANCE")
|
||||||
|
|
||||||
|
echo "---------------------------------------------------------"
|
||||||
|
echo "Decrypted result:"
|
||||||
|
echo "$DECRYPTED"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm "$TEMP_PYTHON_SCRIPT"
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
ROOT=${ROOT:-$HOME}
|
ROOT=${ROOT:-$HOME}
|
||||||
|
|
||||||
DEFAULT_NODE="127.0.0.1:26657"
|
DEFAULT_NODE="127.0.0.1:26657"
|
||||||
|
@ -19,58 +17,129 @@ CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
WSURL="ws://$NODE_URL/websocket"
|
WSURL="ws://$NODE_URL/websocket"
|
||||||
|
|
||||||
SUBSCRIBE="{\"jsonrpc\":\"2.0\",\"method\":\"subscribe\",\"params\":[\"execute._contract_address = '$CONTRACT' AND wasm-transfer.action = 'user'\"],\"id\":1}"
|
SUBSCRIBE_TRANSFER="{\"jsonrpc\":\"2.0\",\"method\":\"subscribe\",\"params\":[\"execute._contract_address = '$CONTRACT' AND wasm-transfer.action = 'user'\"],\"id\":1}"
|
||||||
|
SUBSCRIBE_QUERY="{\"jsonrpc\":\"2.0\",\"method\":\"subscribe\",\"params\":[\"execute._contract_address = '$CONTRACT' AND wasm-query_balance.query = 'user'\"],\"id\":2}"
|
||||||
|
|
||||||
echo $SUBSCRIBE
|
# Attestation constants
|
||||||
|
IAS_API_KEY="669244b3e6364b5888289a11d2a1726d"
|
||||||
echo "--------------------------------------------------------"
|
RA_CLIENT_SPID="51CAF5A48B450D624AEFE3286D314894"
|
||||||
echo "subscribe to events"
|
QUOTE_FILE="/tmp/${USER}_test.quote"
|
||||||
|
REPORT_FILE="/tmp/${USER}_datareport"
|
||||||
|
REPORT_SIG_FILE="/tmp/${USER}_datareportsig"
|
||||||
|
|
||||||
# cat keeps the stdin open so websocat doesnt close
|
# cat keeps the stdin open so websocat doesnt close
|
||||||
(echo "$SUBSCRIBE"; cat) | websocat $WSURL | while read msg; do
|
(echo "$SUBSCRIBE_TRANSFER"; echo "$SUBSCRIBE_QUERY"; cat) | websocat $WSURL | while read msg; do
|
||||||
if [[ "$msg" == '{"jsonrpc":"2.0","id":1,"result":{}}' ]]; then
|
if [[ "$msg" == '{"jsonrpc":"2.0","id":1,"result":{}}' ]] || [[ "$msg" == '{"jsonrpc":"2.0","id":2,"result":{}}' ]]; then
|
||||||
echo "... subscribed"
|
|
||||||
echo "---------------------------------------------------------"
|
echo "---------------------------------------------------------"
|
||||||
|
echo "... subscribed to $msg"
|
||||||
echo "... waiting for event"
|
echo "... waiting for event"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if echo "$msg" | jq 'has("error")' > /dev/null; then
|
# TODO - Some reason this is saying ERROR when its fine, needs to be fixed or removed
|
||||||
echo "... error msg $msg"
|
#if echo "$msg" | sed 's/"log":"\[.*\]"/"log":"<invalid_json>"/' | jq 'has("error")' > /dev/null; then
|
||||||
|
# echo "... error msg $msg"
|
||||||
|
# echo "---------------------------------------------------------"
|
||||||
|
# echo "... waiting for event"
|
||||||
|
# continue
|
||||||
|
#fi
|
||||||
|
|
||||||
|
CLEAN_MSG=$(echo "$msg" | sed 's/"log":"\[.*\]"/"log":"<invalid_json>"/' | jq '.result.events')
|
||||||
|
|
||||||
|
if echo "$CLEAN_MSG" | grep -q 'wasm-transfer'; then
|
||||||
echo "---------------------------------------------------------"
|
echo "---------------------------------------------------------"
|
||||||
echo "... waiting for event"
|
echo "... received wasm-transfer event!"
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
echo "... received event! "
|
|
||||||
echo $msg
|
|
||||||
|
|
||||||
echo "... fetching requests"
|
echo "... fetching requests"
|
||||||
|
|
||||||
REQUESTS=$($CMD query wasm contract-state raw $CONTRACT $(printf '%s' "requests" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
REQUESTS=$($CMD query wasm contract-state raw $CONTRACT $(printf '%s' "requests" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
||||||
STATE=$($CMD query wasm contract-state raw $CONTRACT $(printf '%s' "state" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
STATE=$($CMD query wasm contract-state raw $CONTRACT $(printf '%s' "state" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
||||||
|
|
||||||
export ENCLAVE_REQUEST=$(jq -nc --argjson requests "$REQUESTS" --argjson state $STATE '$ARGS.named')
|
export ENCLAVE_REQUEST=$(jq -nc --argjson requests "$REQUESTS" --argjson state $STATE '$ARGS.named')
|
||||||
echo $ENCLAVE_REQUEST | jq .
|
|
||||||
|
|
||||||
export REQUEST_MSG=$(jq -nc --arg message "$ENCLAVE_REQUEST" '$ARGS.named')
|
export REQUEST_MSG=$(jq -nc --arg message "$ENCLAVE_REQUEST" '$ARGS.named')
|
||||||
|
|
||||||
cd $ROOT/cycles-quartz/apps/transfers/enclave
|
cd $ROOT/cycles-quartz/apps/transfers/enclave
|
||||||
|
|
||||||
echo "... executing transfer"
|
echo "... executing transfer"
|
||||||
export UPDATE=$(grpcurl -plaintext -import-path ./proto/ -proto transfers.proto -d "$REQUEST_MSG" "127.0.0.1:$QUARTZ_PORT" transfers.Settlement/Run | jq .message | jq -R 'fromjson | fromjson' | jq -c )
|
export ATTESTED_MSG=$(grpcurl -plaintext -import-path ./proto/ -proto transfers.proto -d "$REQUEST_MSG" "127.0.0.1:$QUARTZ_PORT" transfers.Settlement/Run | jq .message | jq -R 'fromjson | fromjson' | jq -c )
|
||||||
|
QUOTE=$(echo "$ATTESTED_MSG" | jq -c '.attestation')
|
||||||
|
MSG=$(echo "$ATTESTED_MSG" | jq -c '.msg')
|
||||||
|
|
||||||
|
echo "... getting report"
|
||||||
|
echo -n "$QUOTE" | xxd -r -p - > "$QUOTE_FILE"
|
||||||
|
gramine-sgx-ias-request report -g "$RA_CLIENT_SPID" -k "$IAS_API_KEY" -q "$QUOTE_FILE" -r "$REPORT_FILE" -s "$REPORT_SIG_FILE" > /dev/null 2>&1
|
||||||
|
REPORT=$(cat "$REPORT_FILE")
|
||||||
|
REPORTSIG=$(cat "$REPORT_SIG_FILE" | tr -d '\r')
|
||||||
|
|
||||||
echo "... submitting update"
|
echo "... submitting update"
|
||||||
|
export EXECUTE=$(jq -nc --argjson update "$(jq -nc --argjson msg "$MSG" --argjson attestation \
|
||||||
$CMD tx wasm execute $CONTRACT "{\"update\": "$UPDATE" }" --chain-id testing --from admin --node http://$NODE_URL -y
|
"$(jq -nc --argjson report "$(jq -nc --argjson report "$REPORT" --arg reportsig "$REPORTSIG" '$ARGS.named')" '$ARGS.named')" \
|
||||||
|
'$ARGS.named')" '$ARGS.named')
|
||||||
|
echo $EXECUTE | jq '.'
|
||||||
|
$CMD tx wasm execute "$CONTRACT" "$EXECUTE" --from admin --chain-id testing -y --gas 2000000
|
||||||
|
|
||||||
echo " ... done"
|
echo " ... done"
|
||||||
echo "---------------------------------------------------------"
|
echo "---------------------------------------------------------"
|
||||||
echo "... waiting for event"
|
echo "... waiting for event"
|
||||||
|
elif echo "$CLEAN_MSG" | grep -q 'wasm-query_balance'; then
|
||||||
|
echo "... received wasm-query_balance event!"
|
||||||
|
echo "... fetching state"
|
||||||
|
|
||||||
|
STATE=$($CMD query wasm contract-state raw $CONTRACT $(printf '%s' "state" | hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)
|
||||||
|
|
||||||
|
# Extract the address from the event
|
||||||
|
ADDRESS=$(echo "$msg" | sed 's/"log":"\[.*\]"/"log":"<invalid_json>"/' | jq -r '.result.events["message.sender"]'[0])
|
||||||
|
|
||||||
|
EPHEMERAL_PUBKEY=$(echo "$msg" | sed 's/"log":"\[.*\]"/"log":"<invalid_json>"/' | jq -r '.result.events["wasm-query_balance.emphemeral_pubkey"]'[0])
|
||||||
|
|
||||||
|
# Create the enclave request with state and address
|
||||||
|
export ENCLAVE_REQUEST=$(jq -nc --argjson state "$STATE" --arg address "$ADDRESS" --arg ephemeral_pubkey "$EPHEMERAL_PUBKEY" '$ARGS.named')
|
||||||
|
export REQUEST_MSG=$(jq -nc --arg message "$ENCLAVE_REQUEST" '$ARGS.named')
|
||||||
|
|
||||||
|
cd $ROOT/cycles-quartz/apps/transfers/enclave
|
||||||
|
|
||||||
|
echo "... executing query balance"
|
||||||
|
ATTESTED_MSG=$(grpcurl -plaintext -import-path ./proto/ -proto transfers.proto -d "$REQUEST_MSG" "127.0.0.1:$QUARTZ_PORT" transfers.Settlement/Query | jq -r '.message | fromjson')
|
||||||
|
echo "atts msg"
|
||||||
|
echo $ATTESTED_MSG
|
||||||
|
QUOTE=$(echo "$ATTESTED_MSG" | jq -c '.attestation')
|
||||||
|
MSG=$(echo "$ATTESTED_MSG" | jq -c '.msg')
|
||||||
|
echo "quote"
|
||||||
|
echo $QUOTE
|
||||||
|
echo "msg"
|
||||||
|
echo $MSG
|
||||||
|
|
||||||
|
echo -n "$QUOTE" | xxd -r -p - > "$QUOTE_FILE"
|
||||||
|
gramine-sgx-ias-request report -g "$RA_CLIENT_SPID" -k "$IAS_API_KEY" -q "$QUOTE_FILE" -r "$REPORT_FILE" -s "$REPORT_SIG_FILE" > /dev/null 2>&1
|
||||||
|
REPORT=$(cat "$REPORT_FILE")
|
||||||
|
REPORTSIG=$(cat "$REPORT_SIG_FILE" | tr -d '\r')
|
||||||
|
|
||||||
|
echo "... submitting update"
|
||||||
|
|
||||||
|
# Create the QueryResponseMsg structure with address inside the msg
|
||||||
|
export QUERY_RESPONSE_MSG=$(jq -n \
|
||||||
|
--arg address "$ADDRESS" \
|
||||||
|
--argjson msg "$MSG" \
|
||||||
|
'{address: $address, encrypted_bal: $msg.encrypted_bal}')
|
||||||
|
|
||||||
|
|
||||||
|
# Create the execute message for query_response
|
||||||
|
export EXECUTE=$(jq -nc \
|
||||||
|
--argjson query_response "$(jq -nc \
|
||||||
|
--argjson msg "$QUERY_RESPONSE_MSG" \
|
||||||
|
--argjson attestation "$(jq -nc \
|
||||||
|
--argjson report "$(jq -nc \
|
||||||
|
--argjson report "$REPORT" \
|
||||||
|
--arg reportsig "$REPORTSIG" \
|
||||||
|
'$ARGS.named')" \
|
||||||
|
'$ARGS.named')" \
|
||||||
|
'$ARGS.named')" \
|
||||||
|
'{query_response: $query_response}')
|
||||||
|
|
||||||
|
echo $EXECUTE | jq '.'
|
||||||
|
|
||||||
|
$CMD tx wasm execute "$CONTRACT" "$EXECUTE" --from admin --chain-id testing -y --gas 2000000
|
||||||
|
|
||||||
|
echo " ... done"
|
||||||
|
echo "------------------------------------"
|
||||||
|
echo "... waiting for event"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,11 @@ DIR_QUARTZ_TM_PROVER="$DIR_QUARTZ/utils/tm-prover"
|
||||||
NODE_URL=${NODE_URL:-127.0.0.1:26657}
|
NODE_URL=${NODE_URL:-127.0.0.1:26657}
|
||||||
CMD="wasmd --node http://$NODE_URL"
|
CMD="wasmd --node http://$NODE_URL"
|
||||||
|
|
||||||
|
# Use the QUARTZ_PORT environment variable if set, otherwise default to 11090
|
||||||
|
QUARTZ_PORT="${QUARTZ_PORT:-11090}"
|
||||||
|
|
||||||
echo "--------------------------------------------------------"
|
echo "--------------------------------------------------------"
|
||||||
|
echo "QUARTZ_PORT is set to: $QUARTZ_PORT"
|
||||||
echo "set trusted hash"
|
echo "set trusted hash"
|
||||||
|
|
||||||
cd "$DIR_QUARTZ_TM_PROVER"
|
cd "$DIR_QUARTZ_TM_PROVER"
|
||||||
|
@ -51,6 +54,7 @@ gramine-manifest \
|
||||||
-Dquartz_dir="$(pwd)" \
|
-Dquartz_dir="$(pwd)" \
|
||||||
-Dtrusted_height="$TRUSTED_HEIGHT" \
|
-Dtrusted_height="$TRUSTED_HEIGHT" \
|
||||||
-Dtrusted_hash="$TRUSTED_HASH" \
|
-Dtrusted_hash="$TRUSTED_HASH" \
|
||||||
|
-Dgramine_port="$QUARTZ_PORT" \
|
||||||
quartz.manifest.template quartz.manifest
|
quartz.manifest.template quartz.manifest
|
||||||
|
|
||||||
echo "... sign manifest"
|
echo "... sign manifest"
|
||||||
|
|
Loading…
Reference in a new issue