Adapt cw-tee-mtcs to use quartz (#12)
This commit is contained in:
parent
b4e5f01cd1
commit
57b2a050ff
16 changed files with 507 additions and 264 deletions
|
@ -54,5 +54,8 @@ schemars = "0.8.15"
|
||||||
serde = { version = "1.0.189", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.189", default-features = false, features = ["derive"] }
|
||||||
thiserror = { version = "1.0.49" }
|
thiserror = { version = "1.0.49" }
|
||||||
|
|
||||||
|
quartz-cw = { path = "../../packages/quartz-cw" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cw-multi-test = "0.17.0"
|
cw-multi-test = "0.17.0"
|
||||||
|
serde_json = "1.0.113"
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use cosmwasm_std::{
|
use cosmwasm_std::{entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
|
||||||
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
|
|
||||||
};
|
|
||||||
use cw2::set_contract_version;
|
use cw2::set_contract_version;
|
||||||
|
use quartz_cw::handler::RawHandler;
|
||||||
|
|
||||||
use crate::error::ContractError;
|
use crate::error::ContractError;
|
||||||
use crate::msg::execute::{BootstrapKeyManagerMsg, JoinComputeNodeMsg, RegisterEpochKeyMsg};
|
use crate::msg::execute::{SubmitObligationMsg, SubmitSetoffsMsg};
|
||||||
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
|
use crate::msg::QueryMsg;
|
||||||
use crate::state::{State, STATE};
|
use crate::msg::{ExecuteMsg, InstantiateMsg};
|
||||||
|
use crate::state::{current_epoch_key, ObligationsItem, State, OBLIGATIONS_KEY, STATE};
|
||||||
|
|
||||||
// version info for migration info
|
// version info for migration info
|
||||||
const CONTRACT_NAME: &str = "crates.io:cw-tee-mtcs";
|
const CONTRACT_NAME: &str = "crates.io:cw-tee-mtcs";
|
||||||
|
@ -14,17 +14,23 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
pub fn instantiate(
|
pub fn instantiate(
|
||||||
deps: DepsMut,
|
mut deps: DepsMut,
|
||||||
_env: Env,
|
env: Env,
|
||||||
info: MessageInfo,
|
info: MessageInfo,
|
||||||
_msg: InstantiateMsg,
|
msg: InstantiateMsg,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
|
// must be the handled first!
|
||||||
|
msg.0.handle_raw(deps.branch(), &env, &info)?;
|
||||||
|
|
||||||
let state = State {
|
let state = State {
|
||||||
owner: info.sender.to_string(),
|
owner: info.sender.to_string(),
|
||||||
};
|
};
|
||||||
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||||
STATE.save(deps.storage, &state)?;
|
STATE.save(deps.storage, &state)?;
|
||||||
|
|
||||||
|
ObligationsItem::new(¤t_epoch_key(OBLIGATIONS_KEY, deps.storage)?)
|
||||||
|
.save(deps.storage, &Default::default())?;
|
||||||
|
|
||||||
Ok(Response::new()
|
Ok(Response::new()
|
||||||
.add_attribute("method", "instantiate")
|
.add_attribute("method", "instantiate")
|
||||||
.add_attribute("owner", info.sender))
|
.add_attribute("owner", info.sender))
|
||||||
|
@ -33,151 +39,70 @@ pub fn instantiate(
|
||||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
deps: DepsMut,
|
deps: DepsMut,
|
||||||
_env: Env,
|
env: Env,
|
||||||
_info: MessageInfo,
|
info: MessageInfo,
|
||||||
msg: ExecuteMsg,
|
msg: ExecuteMsg,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
match msg {
|
match msg {
|
||||||
ExecuteMsg::BootstrapKeyManager(BootstrapKeyManagerMsg {
|
ExecuteMsg::Quartz(msg) => msg.handle_raw(deps, &env, &info).map_err(Into::into),
|
||||||
compute_mrenclave,
|
ExecuteMsg::SubmitObligation(SubmitObligationMsg { ciphertext, digest }) => {
|
||||||
key_manager_mrenclave,
|
execute::submit_obligation(deps, ciphertext, digest)
|
||||||
tcb_info,
|
|
||||||
}) => {
|
|
||||||
execute::bootstrap_key_manger(deps, compute_mrenclave, key_manager_mrenclave, tcb_info)
|
|
||||||
}
|
}
|
||||||
ExecuteMsg::RegisterEpochKey(RegisterEpochKeyMsg { epoch_key }) => {
|
ExecuteMsg::SubmitSetoffs(SubmitSetoffsMsg { setoffs_enc }) => {
|
||||||
execute::register_epoch_key(deps, epoch_key)
|
execute::submit_setoffs(deps, setoffs_enc)
|
||||||
}
|
}
|
||||||
ExecuteMsg::JoinComputeNode(JoinComputeNodeMsg {
|
|
||||||
io_exchange_key,
|
|
||||||
address,
|
|
||||||
nonce,
|
|
||||||
}) => execute::enqueue_join_request(deps, io_exchange_key, address, nonce),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod execute {
|
pub mod execute {
|
||||||
use cosmwasm_std::{DepsMut, Response};
|
use std::collections::BTreeMap;
|
||||||
use k256::ecdsa::VerifyingKey;
|
|
||||||
|
use cosmwasm_std::{DepsMut, HexBinary, Response};
|
||||||
|
use quartz_cw::state::Hash;
|
||||||
|
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
EpochState, Mrenclave, RawAddress, RawMrenclave, RawNonce, RawPublicKey, RawTcbInfo,
|
current_epoch_key, ObligationsItem, RawCipherText, RawHash, SetoffsItem, OBLIGATIONS_KEY,
|
||||||
SgxState, EPOCH_STATE, SGX_STATE,
|
SETOFFS_KEY,
|
||||||
};
|
};
|
||||||
use crate::state::{Request, REQUESTS};
|
|
||||||
use crate::ContractError;
|
use crate::ContractError;
|
||||||
use crate::ContractError::BadLength;
|
|
||||||
|
|
||||||
pub fn bootstrap_key_manger(
|
pub fn submit_obligation(
|
||||||
deps: DepsMut,
|
deps: DepsMut,
|
||||||
compute_mrenclave: RawMrenclave,
|
ciphertext: HexBinary,
|
||||||
key_manager_mrenclave: RawMrenclave,
|
digest: HexBinary,
|
||||||
tcb_info: RawTcbInfo,
|
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
let _: Mrenclave = hex::decode(&compute_mrenclave)?
|
let _: Hash = digest.to_array()?;
|
||||||
.try_into()
|
|
||||||
.map_err(|_| BadLength)?;
|
|
||||||
let _: Mrenclave = hex::decode(&key_manager_mrenclave)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| BadLength)?;
|
|
||||||
// TODO(hu55a1n1): validate TcbInfo
|
|
||||||
|
|
||||||
let sgx_state = SgxState {
|
// store the `(digest, ciphertext)` tuple
|
||||||
compute_mrenclave: compute_mrenclave.clone(),
|
ObligationsItem::new(¤t_epoch_key(OBLIGATIONS_KEY, deps.storage)?).update(
|
||||||
key_manager_mrenclave: key_manager_mrenclave.clone(),
|
deps.storage,
|
||||||
tcb_info: tcb_info.clone(),
|
|mut obligations| {
|
||||||
};
|
if let Some(_duplicate) = obligations.insert(digest.clone(), ciphertext.clone()) {
|
||||||
|
return Err(ContractError::DuplicateEntry);
|
||||||
if SGX_STATE.exists(deps.storage) {
|
}
|
||||||
return Err(ContractError::Unauthorized);
|
Ok(obligations)
|
||||||
}
|
},
|
||||||
|
)?;
|
||||||
SGX_STATE.save(deps.storage, &sgx_state)?;
|
|
||||||
|
|
||||||
Ok(Response::new()
|
Ok(Response::new()
|
||||||
.add_attribute("action", "bootstrap_key_manger")
|
.add_attribute("action", "submit_obligation")
|
||||||
.add_attribute("compute_mrenclave", compute_mrenclave)
|
.add_attribute("digest", digest.to_string())
|
||||||
.add_attribute("key_manager_mrenclave", key_manager_mrenclave)
|
.add_attribute("ciphertext", ciphertext.to_string()))
|
||||||
.add_attribute("tcb_info", tcb_info))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_epoch_key(
|
pub fn submit_setoffs(
|
||||||
deps: DepsMut,
|
deps: DepsMut,
|
||||||
epoch_key: RawPublicKey,
|
setoffs_enc: BTreeMap<RawHash, RawCipherText>,
|
||||||
) -> Result<Response, ContractError> {
|
) -> Result<Response, ContractError> {
|
||||||
let _ = VerifyingKey::from_sec1_bytes(&hex::decode(&epoch_key)?)?;
|
// store the `BTreeMap<RawHash, RawCipherText>`
|
||||||
|
SetoffsItem::new(¤t_epoch_key(SETOFFS_KEY, deps.storage)?)
|
||||||
|
.save(deps.storage, &setoffs_enc)?;
|
||||||
|
|
||||||
let epoch_state = EpochState {
|
Ok(Response::new().add_attribute("action", "submit_setoffs"))
|
||||||
epoch_key: epoch_key.clone(),
|
|
||||||
};
|
|
||||||
EPOCH_STATE.save(deps.storage, &epoch_state)?;
|
|
||||||
|
|
||||||
Ok(Response::new()
|
|
||||||
.add_attribute("action", "register_epoch_key")
|
|
||||||
.add_attribute("epoch_key", epoch_key))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enqueue_join_request(
|
|
||||||
deps: DepsMut,
|
|
||||||
io_exchange_key: RawPublicKey,
|
|
||||||
address: RawAddress,
|
|
||||||
nonce: RawNonce,
|
|
||||||
) -> Result<Response, ContractError> {
|
|
||||||
let _ = VerifyingKey::from_sec1_bytes(&hex::decode(&io_exchange_key)?)?;
|
|
||||||
let _ = deps.api.addr_validate(&address)?;
|
|
||||||
let _ = hex::decode(&nonce);
|
|
||||||
|
|
||||||
let mut requests = REQUESTS.may_load(deps.storage)?.unwrap_or_default();
|
|
||||||
requests.push((
|
|
||||||
nonce,
|
|
||||||
Request::JoinComputeNode((io_exchange_key.clone(), address)),
|
|
||||||
));
|
|
||||||
REQUESTS.save(deps.storage, &requests)?;
|
|
||||||
|
|
||||||
Ok(Response::new()
|
|
||||||
.add_attribute("action", "enqueue_request")
|
|
||||||
.add_attribute("io_exchange_key", io_exchange_key))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
||||||
match msg {
|
match msg {}
|
||||||
QueryMsg::GetSgxState {} => to_json_binary(&query::get_sgx_state(deps)?),
|
|
||||||
QueryMsg::GetEpochState {} => to_json_binary(&query::get_epoch_state(deps)?),
|
|
||||||
QueryMsg::GetRequests {} => to_json_binary(&query::get_requests(deps)?),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod query {
|
|
||||||
use cosmwasm_std::{Deps, StdResult};
|
|
||||||
|
|
||||||
use crate::msg::query::{GetEpochStateResponse, GetRequestsResponse, GetSgxStateResponse};
|
|
||||||
use crate::state::{EpochState, SgxState, EPOCH_STATE, REQUESTS, SGX_STATE};
|
|
||||||
|
|
||||||
pub fn get_sgx_state(deps: Deps) -> StdResult<GetSgxStateResponse> {
|
|
||||||
let SgxState {
|
|
||||||
compute_mrenclave,
|
|
||||||
key_manager_mrenclave,
|
|
||||||
..
|
|
||||||
} = SGX_STATE.load(deps.storage)?;
|
|
||||||
Ok(GetSgxStateResponse {
|
|
||||||
compute_mrenclave,
|
|
||||||
key_manager_mrenclave,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_epoch_state(deps: Deps) -> StdResult<GetEpochStateResponse> {
|
|
||||||
let EpochState { epoch_key } = EPOCH_STATE.load(deps.storage)?;
|
|
||||||
Ok(GetEpochStateResponse { epoch_key })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_requests(deps: Deps) -> StdResult<GetRequestsResponse> {
|
|
||||||
Ok(GetRequestsResponse {
|
|
||||||
requests: REQUESTS.load(deps.storage)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use cosmwasm_std::StdError;
|
use cosmwasm_std::StdError;
|
||||||
use hex::FromHexError;
|
use hex::FromHexError;
|
||||||
use k256::ecdsa::Error as K256Error;
|
use k256::ecdsa::Error as K256Error;
|
||||||
|
use quartz_cw::error::Error as QuartzError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -8,14 +9,20 @@ pub enum ContractError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Std(#[from] StdError),
|
Std(#[from] StdError),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
Quartz(#[from] QuartzError),
|
||||||
|
|
||||||
#[error("Unauthorized")]
|
#[error("Unauthorized")]
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
|
|
||||||
|
#[error("Duplicate entry found")]
|
||||||
|
DuplicateEntry,
|
||||||
|
|
||||||
#[error("Not Secp256K1")]
|
#[error("Not Secp256K1")]
|
||||||
K256(K256Error),
|
K256(K256Error),
|
||||||
|
|
||||||
#[error("Invalid hex")]
|
#[error("Invalid hex")]
|
||||||
Hex(FromHexError),
|
Hex(#[from] FromHexError),
|
||||||
|
|
||||||
#[error("Invalid length")]
|
#[error("Invalid length")]
|
||||||
BadLength,
|
BadLength,
|
||||||
|
@ -23,12 +30,6 @@ pub enum ContractError {
|
||||||
|
|
||||||
impl From<K256Error> for ContractError {
|
impl From<K256Error> for ContractError {
|
||||||
fn from(e: K256Error) -> Self {
|
fn from(e: K256Error) -> Self {
|
||||||
ContractError::K256(e)
|
Self::K256(e)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FromHexError> for ContractError {
|
|
||||||
fn from(e: FromHexError) -> Self {
|
|
||||||
ContractError::Hex(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +1,134 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use cosmwasm_schema::{cw_serde, QueryResponses};
|
use cosmwasm_schema::{cw_serde, QueryResponses};
|
||||||
|
use quartz_cw::prelude::*;
|
||||||
|
|
||||||
|
use crate::state::{RawCipherText, RawHash};
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct InstantiateMsg;
|
#[serde(transparent)]
|
||||||
|
pub struct InstantiateMsg(pub QuartzInstantiateMsg);
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum ExecuteMsg {
|
pub enum ExecuteMsg {
|
||||||
BootstrapKeyManager(execute::BootstrapKeyManagerMsg),
|
Quartz(QuartzExecuteMsg),
|
||||||
RegisterEpochKey(execute::RegisterEpochKeyMsg),
|
SubmitObligation(execute::SubmitObligationMsg),
|
||||||
JoinComputeNode(execute::JoinComputeNodeMsg),
|
SubmitSetoffs(execute::SubmitSetoffsMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod execute {
|
pub mod execute {
|
||||||
|
use cosmwasm_std::HexBinary;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct BootstrapKeyManagerMsg {
|
pub struct SubmitObligationMsg {
|
||||||
pub compute_mrenclave: String,
|
pub ciphertext: HexBinary,
|
||||||
pub key_manager_mrenclave: String,
|
pub digest: HexBinary,
|
||||||
pub tcb_info: String,
|
// pub signatures: [HexBinary; 2],
|
||||||
|
// pub proof: π
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct RegisterEpochKeyMsg {
|
pub struct SubmitSetoffsMsg {
|
||||||
pub epoch_key: String,
|
pub setoffs_enc: BTreeMap<RawHash, RawCipherText>,
|
||||||
}
|
// pub proof: π,
|
||||||
|
|
||||||
#[cw_serde]
|
|
||||||
pub struct JoinComputeNodeMsg {
|
|
||||||
pub io_exchange_key: String,
|
|
||||||
pub address: String,
|
|
||||||
pub nonce: String,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[derive(QueryResponses)]
|
#[derive(QueryResponses)]
|
||||||
pub enum QueryMsg {
|
pub enum QueryMsg {}
|
||||||
#[returns(query::GetSgxStateResponse)]
|
|
||||||
GetSgxState {},
|
|
||||||
#[returns(query::GetEpochStateResponse)]
|
|
||||||
GetEpochState {},
|
|
||||||
#[returns(query::GetRequestsResponse)]
|
|
||||||
GetRequests {},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod query {
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::state::{RawMrenclave, RawNonce, RawPublicKey, Request};
|
#[test]
|
||||||
|
fn test_serde_instantiate_msg() {
|
||||||
#[cw_serde]
|
let _: InstantiateMsg = serde_json::from_str(
|
||||||
pub struct GetSgxStateResponse {
|
r#"{
|
||||||
pub compute_mrenclave: RawMrenclave,
|
"msg": {
|
||||||
pub key_manager_mrenclave: RawMrenclave,
|
"config": {
|
||||||
|
"mr_enclave": "1bfb949d235f61e5dc40f874ba3e9c36adef1e7a521b4b5f70e10fb1dc803251",
|
||||||
|
"epoch_duration": {
|
||||||
|
"secs": 43200,
|
||||||
|
"nanos": 0
|
||||||
|
},
|
||||||
|
"light_client_opts": {
|
||||||
|
"chain_id": "testing",
|
||||||
|
"trusted_height": 1,
|
||||||
|
"trusted_hash": "a1d115ba3a5e9fcc12ed68a9d8669159e9085f6f96ec26619f5c7ceb4ee02869",
|
||||||
|
"trust_threshold": [
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"trusting_period": 1209600,
|
||||||
|
"max_clock_drift": 5,
|
||||||
|
"max_block_lag": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attestation": {
|
||||||
|
"report": {
|
||||||
|
"report": {
|
||||||
|
"id": "5246688123689513540899231107533660789",
|
||||||
|
"timestamp": "2024-02-07T17:06:23.913745",
|
||||||
|
"version": 4,
|
||||||
|
"epidPseudonym": "+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=",
|
||||||
|
"advisoryURL": "https://security-center.intel.com",
|
||||||
|
"advisoryIDs": [
|
||||||
|
"INTEL-SA-00161",
|
||||||
|
"INTEL-SA-00219",
|
||||||
|
"INTEL-SA-00289",
|
||||||
|
"INTEL-SA-00334",
|
||||||
|
"INTEL-SA-00615"
|
||||||
|
],
|
||||||
|
"isvEnclaveQuoteStatus": "CONFIGURATION_AND_SW_HARDENING_NEEDED",
|
||||||
|
"platformInfoBlob": "150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1",
|
||||||
|
"isvEnclaveQuoteBody": "AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
},
|
||||||
|
"reportsig": "YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
).expect("failed to deserialize hardcoded quartz instantiate msg");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[test]
|
||||||
pub struct GetEpochStateResponse {
|
fn test_serde_execute_msg() {
|
||||||
pub epoch_key: RawPublicKey,
|
let _: ExecuteMsg = serde_json::from_str(
|
||||||
}
|
r#"{
|
||||||
|
"quartz": {
|
||||||
#[cw_serde]
|
"session_create": {
|
||||||
pub struct GetRequestsResponse {
|
"msg": {
|
||||||
pub requests: Vec<(RawNonce, Request)>,
|
"nonce": "425d87f8620e1dedeee70590cc55b164b8f01480ee59e0b1da35436a2f7c2777"
|
||||||
|
},
|
||||||
|
"attestation": {
|
||||||
|
"report": {
|
||||||
|
"report": {
|
||||||
|
"id": "5246688123689513540899231107533660789",
|
||||||
|
"timestamp": "2024-02-07T17:06:23.913745",
|
||||||
|
"version": 4,
|
||||||
|
"epidPseudonym": "+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=",
|
||||||
|
"advisoryURL": "https://security-center.intel.com",
|
||||||
|
"advisoryIDs": [
|
||||||
|
"INTEL-SA-00161",
|
||||||
|
"INTEL-SA-00219",
|
||||||
|
"INTEL-SA-00289",
|
||||||
|
"INTEL-SA-00334",
|
||||||
|
"INTEL-SA-00615"
|
||||||
|
],
|
||||||
|
"isvEnclaveQuoteStatus": "CONFIGURATION_AND_SW_HARDENING_NEEDED",
|
||||||
|
"platformInfoBlob": "150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1",
|
||||||
|
"isvEnclaveQuoteBody": "AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
},
|
||||||
|
"reportsig": "YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
).expect("failed to deserialize hardcoded quartz msg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,25 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
|
use cosmwasm_std::{HexBinary, StdError, Storage};
|
||||||
use cw_storage_plus::Item;
|
use cw_storage_plus::Item;
|
||||||
|
use quartz_cw::state::EPOCH_COUNTER;
|
||||||
|
|
||||||
pub type RawNonce = String;
|
pub type RawHash = HexBinary;
|
||||||
pub type RawPublicKey = String;
|
pub type RawCipherText = HexBinary;
|
||||||
pub type RawAddress = String;
|
|
||||||
pub type RawMrenclave = String;
|
|
||||||
pub type RawTcbInfo = String;
|
|
||||||
|
|
||||||
pub type Mrenclave = [u8; 32];
|
pub type ObligationsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>;
|
||||||
|
pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>;
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub owner: String,
|
pub owner: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
|
||||||
pub enum Request {
|
|
||||||
JoinComputeNode((RawPublicKey, RawAddress)),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cw_serde]
|
|
||||||
pub struct SgxState {
|
|
||||||
pub compute_mrenclave: RawMrenclave,
|
|
||||||
pub key_manager_mrenclave: RawMrenclave,
|
|
||||||
pub tcb_info: RawTcbInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cw_serde]
|
|
||||||
pub struct EpochState {
|
|
||||||
pub epoch_key: RawPublicKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const STATE: Item<State> = Item::new("state");
|
pub const STATE: Item<State> = Item::new("state");
|
||||||
pub const REQUESTS: Item<Vec<(RawNonce, Request)>> = Item::new("requests");
|
pub const OBLIGATIONS_KEY: &str = "obligations";
|
||||||
pub const SGX_STATE: Item<SgxState> = Item::new("sgx_state");
|
pub const SETOFFS_KEY: &str = "setoffs";
|
||||||
pub const EPOCH_STATE: Item<EpochState> = Item::new("epoch_state");
|
|
||||||
|
pub fn current_epoch_key(key: &str, storage: &dyn Storage) -> Result<String, StdError> {
|
||||||
|
Ok(format!("{}/{key}", EPOCH_COUNTER.load(storage)?))
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ cosmwasm-schema = "1.4.0"
|
||||||
cosmwasm-std = "1.4.0"
|
cosmwasm-std = "1.4.0"
|
||||||
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
|
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
|
||||||
serde = { version = "1.0.188", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.188", default-features = false, features = ["derive"] }
|
||||||
|
serde_json = "1.0.94"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0.57"
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,14 @@ use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::handler::Handler;
|
use crate::handler::Handler;
|
||||||
|
use crate::msg::execute::attested::Attestation;
|
||||||
|
use crate::msg::execute::attested::HasUserData;
|
||||||
use crate::msg::execute::Execute;
|
use crate::msg::execute::Execute;
|
||||||
|
|
||||||
impl Handler for Execute {
|
impl<A> Handler for Execute<A>
|
||||||
|
where
|
||||||
|
A: Handler + HasUserData + Attestation,
|
||||||
|
{
|
||||||
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
|
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
|
||||||
match self {
|
match self {
|
||||||
Execute::SessionCreate(msg) => msg.handle(deps, env, info),
|
Execute::SessionCreate(msg) => msg.handle(deps, env, info),
|
||||||
|
|
|
@ -3,7 +3,9 @@ use quartz_tee_ra::{verify_epid_attestation, Error as RaVerificationError};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::handler::Handler;
|
use crate::handler::Handler;
|
||||||
use crate::msg::execute::attested::{Attestation, Attested, EpidAttestation, HasUserData};
|
use crate::msg::execute::attested::{
|
||||||
|
Attestation, Attested, EpidAttestation, HasUserData, MockAttestation,
|
||||||
|
};
|
||||||
use crate::state::CONFIG;
|
use crate::state::CONFIG;
|
||||||
|
|
||||||
impl Handler for EpidAttestation {
|
impl Handler for EpidAttestation {
|
||||||
|
@ -13,10 +15,25 @@ impl Handler for EpidAttestation {
|
||||||
_env: &Env,
|
_env: &Env,
|
||||||
_info: &MessageInfo,
|
_info: &MessageInfo,
|
||||||
) -> Result<Response, Error> {
|
) -> Result<Response, Error> {
|
||||||
let (report, mr_enclave, user_data) = self.into_tuple();
|
// attestation handler MUST verify that the user_data and mr_enclave match the config/msg
|
||||||
verify_epid_attestation(report, mr_enclave, user_data)
|
verify_epid_attestation(
|
||||||
.map(|_| Response::default())
|
self.clone().into_report(),
|
||||||
.map_err(Error::RaVerification)
|
self.mr_enclave(),
|
||||||
|
self.user_data(),
|
||||||
|
)
|
||||||
|
.map(|_| Response::default())
|
||||||
|
.map_err(Error::RaVerification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for MockAttestation {
|
||||||
|
fn handle(
|
||||||
|
self,
|
||||||
|
_deps: DepsMut<'_>,
|
||||||
|
_env: &Env,
|
||||||
|
_info: &MessageInfo,
|
||||||
|
) -> Result<Response, Error> {
|
||||||
|
Ok(Response::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,12 +55,17 @@ where
|
||||||
|
|
||||||
if let Some(config) = CONFIG.may_load(deps.storage)? {
|
if let Some(config) = CONFIG.may_load(deps.storage)? {
|
||||||
// if we weren't able to load then the context was from InstantiateMsg so we don't fail
|
// if we weren't able to load then the context was from InstantiateMsg so we don't fail
|
||||||
if *config.mr_enclave() != attestation.mr_enclave() {
|
// in such cases, the InstantiateMsg handler will verify that the mr_enclave matches
|
||||||
|
if config.mr_enclave() != attestation.mr_enclave() {
|
||||||
return Err(RaVerificationError::MrEnclaveMismatch.into());
|
return Err(RaVerificationError::MrEnclaveMismatch.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Handler::handle(attestation, deps.branch(), env, info)?;
|
// handle message first, this has 2 benefits -
|
||||||
Handler::handle(msg, deps, env, info)
|
// 1. we avoid (the more expensive) attestation verification if the message handler fails
|
||||||
|
// 2. we allow the message handler to make changes to the config so that the attestation
|
||||||
|
// handler can use those changes, e.g. InstantiateMsg
|
||||||
|
Handler::handle(msg, deps.branch(), env, info)?;
|
||||||
|
Handler::handle(attestation, deps, env, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,34 @@ use quartz_tee_ra::Error as RaVerificationError;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::handler::Handler;
|
use crate::handler::Handler;
|
||||||
use crate::msg::execute::attested::{Attestation, EpidAttestation};
|
use crate::msg::execute::attested::{Attestation, EpidAttestation, MockAttestation};
|
||||||
use crate::msg::instantiate::{CoreInstantiate, Instantiate};
|
use crate::msg::instantiate::{CoreInstantiate, Instantiate};
|
||||||
use crate::state::Config;
|
|
||||||
use crate::state::CONFIG;
|
use crate::state::CONFIG;
|
||||||
|
use crate::state::{RawConfig, EPOCH_COUNTER};
|
||||||
|
|
||||||
impl Handler for Instantiate<EpidAttestation> {
|
impl Handler for Instantiate<EpidAttestation> {
|
||||||
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
|
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
|
||||||
if self.0.msg().mr_enclave() != self.0.attestation().mr_enclave() {
|
if self.0.msg().config().mr_enclave() != self.0.attestation().mr_enclave() {
|
||||||
return Err(RaVerificationError::MrEnclaveMismatch.into());
|
return Err(RaVerificationError::MrEnclaveMismatch.into());
|
||||||
}
|
}
|
||||||
self.0.handle(deps, env, info)
|
self.0.handle(deps, env, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Handler for Instantiate<MockAttestation> {
|
||||||
|
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
|
||||||
|
self.0.handle(deps, env, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler for CoreInstantiate {
|
impl Handler for CoreInstantiate {
|
||||||
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
|
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
|
||||||
CONFIG
|
CONFIG
|
||||||
.save(deps.storage, &Config::new(self.mr_enclave()))
|
.save(deps.storage, &RawConfig::from(self.config().clone()))
|
||||||
.map_err(Error::Std)?;
|
.map_err(Error::Std)?;
|
||||||
|
|
||||||
|
EPOCH_COUNTER.save(deps.storage, &1).map_err(Error::Std)?;
|
||||||
|
|
||||||
Ok(Response::new().add_attribute("action", "instantiate"))
|
Ok(Response::new().add_attribute("action", "instantiate"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,9 +55,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> HasDomainType for RawExecute<A>
|
impl<RA> HasDomainType for RawExecute<RA>
|
||||||
where
|
where
|
||||||
A: HasDomainType,
|
RA: HasDomainType,
|
||||||
{
|
{
|
||||||
type DomainType = Execute<A::DomainType>;
|
type DomainType = Execute<RA::DomainType>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::{HexBinary, StdError};
|
use cosmwasm_std::StdError;
|
||||||
use quartz_tee_ra::IASReport;
|
use quartz_tee_ra::IASReport;
|
||||||
|
|
||||||
use crate::msg::HasDomainType;
|
use crate::msg::HasDomainType;
|
||||||
|
@ -12,6 +12,10 @@ pub struct Attested<M, A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M, A> Attested<M, A> {
|
impl<M, A> Attested<M, A> {
|
||||||
|
pub fn new(msg: M, attestation: A) -> Self {
|
||||||
|
Self { msg, attestation }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_tuple(self) -> (M, A) {
|
pub fn into_tuple(self) -> (M, A) {
|
||||||
let Attested { msg, attestation } = self;
|
let Attested { msg, attestation } = self;
|
||||||
(msg, attestation)
|
(msg, attestation)
|
||||||
|
@ -75,42 +79,29 @@ pub trait HasUserData {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct EpidAttestation {
|
pub struct EpidAttestation {
|
||||||
report: IASReport,
|
report: IASReport,
|
||||||
mr_enclave: MrEnclave,
|
|
||||||
user_data: UserData,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpidAttestation {
|
impl EpidAttestation {
|
||||||
pub fn into_tuple(self) -> (IASReport, MrEnclave, UserData) {
|
pub fn new(report: IASReport) -> Self {
|
||||||
let EpidAttestation {
|
Self { report }
|
||||||
report,
|
|
||||||
mr_enclave,
|
|
||||||
user_data,
|
|
||||||
} = self;
|
|
||||||
(report, mr_enclave, user_data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(&self) -> &IASReport {
|
pub fn into_report(self) -> IASReport {
|
||||||
&self.report
|
self.report
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct RawEpidAttestation {
|
pub struct RawEpidAttestation {
|
||||||
report: IASReport,
|
report: IASReport,
|
||||||
mr_enclave: HexBinary,
|
|
||||||
user_data: HexBinary,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RawEpidAttestation> for EpidAttestation {
|
impl TryFrom<RawEpidAttestation> for EpidAttestation {
|
||||||
type Error = StdError;
|
type Error = StdError;
|
||||||
|
|
||||||
fn try_from(value: RawEpidAttestation) -> Result<Self, Self::Error> {
|
fn try_from(value: RawEpidAttestation) -> Result<Self, Self::Error> {
|
||||||
let mr_enclave = value.mr_enclave.to_array()?;
|
|
||||||
let user_data = value.user_data.to_array()?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
report: value.report,
|
report: value.report,
|
||||||
mr_enclave,
|
|
||||||
user_data,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,8 +110,6 @@ impl From<EpidAttestation> for RawEpidAttestation {
|
||||||
fn from(value: EpidAttestation) -> Self {
|
fn from(value: EpidAttestation) -> Self {
|
||||||
Self {
|
Self {
|
||||||
report: value.report,
|
report: value.report,
|
||||||
mr_enclave: value.mr_enclave.into(),
|
|
||||||
user_data: value.user_data.into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +120,7 @@ impl HasDomainType for RawEpidAttestation {
|
||||||
|
|
||||||
impl HasUserData for EpidAttestation {
|
impl HasUserData for EpidAttestation {
|
||||||
fn user_data(&self) -> UserData {
|
fn user_data(&self) -> UserData {
|
||||||
self.user_data
|
self.report.report.isv_enclave_quote_body.user_data()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +130,42 @@ pub trait Attestation {
|
||||||
|
|
||||||
impl Attestation for EpidAttestation {
|
impl Attestation for EpidAttestation {
|
||||||
fn mr_enclave(&self) -> MrEnclave {
|
fn mr_enclave(&self) -> MrEnclave {
|
||||||
self.report().report.isv_enclave_quote_body.mrenclave()
|
self.report.report.isv_enclave_quote_body.mrenclave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct MockAttestation;
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct RawMockAttestation;
|
||||||
|
|
||||||
|
impl TryFrom<RawMockAttestation> for MockAttestation {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(_value: RawMockAttestation) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MockAttestation> for RawMockAttestation {
|
||||||
|
fn from(_value: MockAttestation) -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasDomainType for RawMockAttestation {
|
||||||
|
type DomainType = MockAttestation;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasUserData for MockAttestation {
|
||||||
|
fn user_data(&self) -> UserData {
|
||||||
|
unimplemented!("MockAttestation handler is a noop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attestation for MockAttestation {
|
||||||
|
fn mr_enclave(&self) -> MrEnclave {
|
||||||
|
unimplemented!("MockAttestation handler is a noop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ pub struct SessionCreate {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionCreate {
|
impl SessionCreate {
|
||||||
|
pub fn new(nonce: Nonce) -> Self {
|
||||||
|
Self { nonce }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_nonce(self) -> Nonce {
|
pub fn into_nonce(self) -> Nonce {
|
||||||
self.nonce
|
self.nonce
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ pub struct SessionSetPubKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionSetPubKey {
|
impl SessionSetPubKey {
|
||||||
|
pub fn new(nonce: Nonce, pub_key: VerifyingKey) -> Self {
|
||||||
|
Self { nonce, pub_key }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_tuple(self) -> (Nonce, VerifyingKey) {
|
pub fn into_tuple(self) -> (Nonce, VerifyingKey) {
|
||||||
(self.nonce, self.pub_key)
|
(self.nonce, self.pub_key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::{HexBinary, StdError};
|
use cosmwasm_std::StdError;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use crate::msg::execute::attested::{
|
use crate::msg::execute::attested::{
|
||||||
Attested, EpidAttestation, HasUserData, RawAttested, RawEpidAttestation,
|
Attested, EpidAttestation, HasUserData, RawAttested, RawEpidAttestation,
|
||||||
};
|
};
|
||||||
use crate::msg::HasDomainType;
|
use crate::msg::HasDomainType;
|
||||||
use crate::state::{MrEnclave, UserData};
|
use crate::state::{Config, RawConfig, UserData};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Instantiate<A = EpidAttestation>(pub(crate) Attested<CoreInstantiate, A>);
|
pub struct Instantiate<A = EpidAttestation>(pub Attested<CoreInstantiate, A>);
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct RawInstantiate<RA = RawEpidAttestation>(RawAttested<RawCoreInstantiate, RA>);
|
pub struct RawInstantiate<RA = RawEpidAttestation>(RawAttested<RawCoreInstantiate, RA>);
|
||||||
|
@ -34,40 +34,47 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasDomainType for RawInstantiate {
|
impl<RA> HasDomainType for RawInstantiate<RA>
|
||||||
type DomainType = Instantiate;
|
where
|
||||||
|
RA: HasDomainType,
|
||||||
|
{
|
||||||
|
type DomainType = Instantiate<RA::DomainType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct CoreInstantiate {
|
pub struct CoreInstantiate {
|
||||||
mr_enclave: MrEnclave,
|
config: Config,
|
||||||
// TODO(hu55a1n1): config - e.g. Epoch duration, light client opts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreInstantiate {
|
impl CoreInstantiate {
|
||||||
pub fn mr_enclave(&self) -> MrEnclave {
|
pub fn new(config: Config) -> Self {
|
||||||
self.mr_enclave
|
Self { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config(&self) -> &Config {
|
||||||
|
&self.config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct RawCoreInstantiate {
|
pub struct RawCoreInstantiate {
|
||||||
mr_enclave: HexBinary,
|
config: RawConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RawCoreInstantiate> for CoreInstantiate {
|
impl TryFrom<RawCoreInstantiate> for CoreInstantiate {
|
||||||
type Error = StdError;
|
type Error = StdError;
|
||||||
|
|
||||||
fn try_from(value: RawCoreInstantiate) -> Result<Self, Self::Error> {
|
fn try_from(value: RawCoreInstantiate) -> Result<Self, Self::Error> {
|
||||||
let mr_enclave = value.mr_enclave.to_array()?;
|
Ok(Self {
|
||||||
Ok(Self { mr_enclave })
|
config: value.config.try_into()?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CoreInstantiate> for RawCoreInstantiate {
|
impl From<CoreInstantiate> for RawCoreInstantiate {
|
||||||
fn from(value: CoreInstantiate) -> Self {
|
fn from(value: CoreInstantiate) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mr_enclave: value.mr_enclave.into(),
|
config: value.config.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +86,10 @@ impl HasDomainType for RawCoreInstantiate {
|
||||||
impl HasUserData for CoreInstantiate {
|
impl HasUserData for CoreInstantiate {
|
||||||
fn user_data(&self) -> UserData {
|
fn user_data(&self) -> UserData {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(self.mr_enclave);
|
hasher.update(
|
||||||
|
serde_json::to_string(&RawConfig::from(self.config.clone()))
|
||||||
|
.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];
|
||||||
|
|
|
@ -1,26 +1,199 @@
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::HexBinary;
|
use cosmwasm_std::{HexBinary, StdError};
|
||||||
use cw_storage_plus::Item;
|
use cw_storage_plus::Item;
|
||||||
use k256::ecdsa::VerifyingKey;
|
use k256::ecdsa::VerifyingKey;
|
||||||
|
|
||||||
pub type MrEnclave = [u8; 32];
|
pub type MrEnclave = [u8; 32];
|
||||||
pub type Nonce = [u8; 32];
|
pub type Nonce = [u8; 32];
|
||||||
pub type UserData = [u8; 64];
|
pub type UserData = [u8; 64];
|
||||||
|
pub type Hash = [u8; 32];
|
||||||
|
pub type Height = u64;
|
||||||
|
pub type TrustThreshold = (u64, u64);
|
||||||
|
|
||||||
#[cw_serde]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
mr_enclave: HexBinary,
|
mr_enclave: MrEnclave,
|
||||||
|
epoch_duration: Duration,
|
||||||
|
light_client_opts: LightClientOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(mr_enclave: MrEnclave) -> Self {
|
pub fn new(
|
||||||
|
mr_enclave: MrEnclave,
|
||||||
|
epoch_duration: Duration,
|
||||||
|
light_client_opts: LightClientOpts,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mr_enclave: mr_enclave.into(),
|
mr_enclave,
|
||||||
|
epoch_duration,
|
||||||
|
light_client_opts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mr_enclave(&self) -> &HexBinary {
|
pub fn light_client_opts(&self) -> &LightClientOpts {
|
||||||
&self.mr_enclave
|
&self.light_client_opts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mr_enclave(&self) -> MrEnclave {
|
||||||
|
self.mr_enclave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct RawConfig {
|
||||||
|
mr_enclave: HexBinary,
|
||||||
|
epoch_duration: Duration,
|
||||||
|
light_client_opts: RawLightClientOpts,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawConfig {
|
||||||
|
pub fn mr_enclave(&self) -> &[u8] {
|
||||||
|
self.mr_enclave.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawConfig> for Config {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawConfig) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
mr_enclave: value.mr_enclave.to_array()?,
|
||||||
|
epoch_duration: value.epoch_duration,
|
||||||
|
light_client_opts: value
|
||||||
|
.light_client_opts
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| StdError::parse_err("light_client_opts", e))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Config> for RawConfig {
|
||||||
|
fn from(value: Config) -> Self {
|
||||||
|
Self {
|
||||||
|
mr_enclave: value.mr_enclave.into(),
|
||||||
|
epoch_duration: value.epoch_duration,
|
||||||
|
light_client_opts: value.light_client_opts.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct LightClientOpts {
|
||||||
|
chain_id: String,
|
||||||
|
trusted_height: Height,
|
||||||
|
trusted_hash: Hash,
|
||||||
|
trust_threshold: TrustThreshold,
|
||||||
|
trusting_period: u64,
|
||||||
|
max_clock_drift: u64,
|
||||||
|
max_block_lag: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightClientOpts {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn new(
|
||||||
|
chain_id: String,
|
||||||
|
trusted_height: Height,
|
||||||
|
trusted_hash: Hash,
|
||||||
|
trust_threshold: TrustThreshold,
|
||||||
|
trusting_period: u64,
|
||||||
|
max_clock_drift: u64,
|
||||||
|
max_block_lag: u64,
|
||||||
|
) -> Result<Self, StdError> {
|
||||||
|
let (numerator, denominator) = (trust_threshold.0, trust_threshold.1);
|
||||||
|
if numerator > denominator {
|
||||||
|
return Err(StdError::generic_err("trust_threshold_too_large"));
|
||||||
|
}
|
||||||
|
if denominator == 0 {
|
||||||
|
return Err(StdError::generic_err("undefined_trust_threshold"));
|
||||||
|
}
|
||||||
|
if 3 * numerator < denominator {
|
||||||
|
return Err(StdError::generic_err("trust_threshold_too_small"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _trusted_height: i64 = trusted_height
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| StdError::generic_err("trusted_height too large"))?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
chain_id,
|
||||||
|
trusted_height,
|
||||||
|
trusted_hash,
|
||||||
|
trust_threshold,
|
||||||
|
trusting_period,
|
||||||
|
max_clock_drift,
|
||||||
|
max_block_lag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chain_id(&self) -> &String {
|
||||||
|
&self.chain_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trusted_height(&self) -> Height {
|
||||||
|
self.trusted_height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trusted_hash(&self) -> &Hash {
|
||||||
|
&self.trusted_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trust_threshold(&self) -> &TrustThreshold {
|
||||||
|
&self.trust_threshold
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trusting_period(&self) -> u64 {
|
||||||
|
self.trusting_period
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_clock_drift(&self) -> u64 {
|
||||||
|
self.max_clock_drift
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_block_lag(&self) -> u64 {
|
||||||
|
self.max_block_lag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct RawLightClientOpts {
|
||||||
|
chain_id: String,
|
||||||
|
trusted_height: u64,
|
||||||
|
trusted_hash: HexBinary,
|
||||||
|
trust_threshold: (u64, u64),
|
||||||
|
trusting_period: u64,
|
||||||
|
max_clock_drift: u64,
|
||||||
|
max_block_lag: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawLightClientOpts> for LightClientOpts {
|
||||||
|
type Error = StdError;
|
||||||
|
|
||||||
|
fn try_from(value: RawLightClientOpts) -> Result<Self, Self::Error> {
|
||||||
|
Self::new(
|
||||||
|
value.chain_id,
|
||||||
|
value.trusted_height,
|
||||||
|
value.trusted_hash.to_array()?,
|
||||||
|
(value.trust_threshold.0, value.trust_threshold.1),
|
||||||
|
value.trusting_period,
|
||||||
|
value.max_clock_drift,
|
||||||
|
value.max_block_lag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LightClientOpts> for RawLightClientOpts {
|
||||||
|
fn from(value: LightClientOpts) -> Self {
|
||||||
|
Self {
|
||||||
|
chain_id: value.chain_id,
|
||||||
|
trusted_height: value.trusted_height,
|
||||||
|
trusted_hash: Vec::<u8>::from(value.trusted_hash).into(),
|
||||||
|
trust_threshold: (value.trust_threshold.0, value.trust_threshold.1),
|
||||||
|
trusting_period: value.trusting_period,
|
||||||
|
max_clock_drift: value.max_clock_drift,
|
||||||
|
max_block_lag: value.max_block_lag,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +219,12 @@ impl Session {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn nonce(&self) -> Nonce {
|
||||||
|
self.nonce.to_array().expect("correct by construction")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CONFIG: Item<'_, Config> = Item::new("quartz_config");
|
pub const CONFIG: Item<'_, RawConfig> = Item::new("quartz_config");
|
||||||
pub const SESSION: Item<'_, Session> = Item::new("quartz_session");
|
pub const SESSION: Item<'_, Session> = Item::new("quartz_session");
|
||||||
|
pub const EPOCH_COUNTER: Item<'_, usize> = Item::new("epoch_counter");
|
||||||
|
|
|
@ -25,6 +25,7 @@ WASM_BIN="$1"
|
||||||
CHAIN_ID=${CHAIN_ID:-testing}
|
CHAIN_ID=${CHAIN_ID:-testing}
|
||||||
LABEL=${LABEL:-bisenzone-mvp}
|
LABEL=${LABEL:-bisenzone-mvp}
|
||||||
COUNT=${COUNT:-0}
|
COUNT=${COUNT:-0}
|
||||||
|
INSTANTIATE_MSG=${INSTANTIATE_MSG:-"{}"}
|
||||||
|
|
||||||
TXFLAG="--chain-id ${CHAIN_ID} --gas-prices 0.0025ucosm --gas auto --gas-adjustment 1.3"
|
TXFLAG="--chain-id ${CHAIN_ID} --gas-prices 0.0025ucosm --gas auto --gas-adjustment 1.3"
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ echo "--------------------------------------------------------"
|
||||||
echo "Label: ${LABEL}"
|
echo "Label: ${LABEL}"
|
||||||
echo "--------------------------------------------------------"
|
echo "--------------------------------------------------------"
|
||||||
|
|
||||||
wasmd tx wasm instantiate "$CODE_ID" "null" --from "$USER_ADDR" --label $LABEL $TXFLAG -y --no-admin 2>&1 > /dev/null
|
wasmd tx wasm instantiate "$CODE_ID" "$INSTANTIATE_MSG" --from "$USER_ADDR" --label $LABEL $TXFLAG -y --no-admin 2>&1 > /dev/null
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🕐 Waiting for contract to be queryable..."
|
echo "🕐 Waiting for contract to be queryable..."
|
||||||
|
|
Loading…
Reference in a new issue