181 lines
5.7 KiB
Rust
181 lines
5.7 KiB
Rust
use std::{
|
|
collections::BTreeMap,
|
|
sync::{Arc, Mutex},
|
|
};
|
|
|
|
use cosmwasm_std::{Addr, HexBinary, Uint128};
|
|
use cw_tee_mtcs::{
|
|
msg::execute::SubmitSetoffsMsg,
|
|
state::{LiquiditySource, LiquiditySourceType, RawHash, SettleOff, Transfer},
|
|
};
|
|
use ecies::decrypt;
|
|
use k256::ecdsa::SigningKey;
|
|
use mtcs::{
|
|
algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs,
|
|
obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs,
|
|
};
|
|
use quartz_common::{
|
|
contract::{msg::execute::attested::RawAttested, state::Config},
|
|
enclave::{attestor::Attestor, server::IntoServer},
|
|
};
|
|
use tonic::{Request, Response, Result as TonicResult, Status};
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
proto::{
|
|
clearing_server::{Clearing, ClearingServer},
|
|
RunClearingRequest, RunClearingResponse,
|
|
},
|
|
types::{ContractObligation, RunClearingMessage},
|
|
};
|
|
|
|
pub type RawCipherText = HexBinary;
|
|
|
|
impl<A: Attestor> IntoServer for MtcsService<A> {
|
|
type Server = ClearingServer<MtcsService<A>>;
|
|
|
|
fn into_server(self) -> Self::Server {
|
|
ClearingServer::new(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MtcsService<A> {
|
|
#[allow(dead_code)]
|
|
config: Config, // TODO: this config is not used anywhere
|
|
sk: Arc<Mutex<Option<SigningKey>>>,
|
|
attestor: A,
|
|
}
|
|
|
|
impl<A> MtcsService<A>
|
|
where
|
|
A: Attestor,
|
|
{
|
|
pub fn new(config: Config, sk: Arc<Mutex<Option<SigningKey>>>, attestor: A) -> Self {
|
|
Self {
|
|
config,
|
|
sk,
|
|
attestor,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tonic::async_trait]
|
|
impl<A> Clearing for MtcsService<A>
|
|
where
|
|
A: Attestor + Send + Sync + 'static,
|
|
{
|
|
async fn run(
|
|
&self,
|
|
request: Request<RunClearingRequest>,
|
|
) -> TonicResult<Response<RunClearingResponse>> {
|
|
// Light client check
|
|
let message: RunClearingMessage = {
|
|
let message = request.into_inner().message;
|
|
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?
|
|
};
|
|
|
|
// let (proof_value, message) = message
|
|
// .verify(self.config.light_client_opts())
|
|
// .map_err(Status::failed_precondition)?;
|
|
|
|
// let proof_value_matches_msg =
|
|
// serde_json::to_string(&message.intents).is_ok_and(|s| s.as_bytes() == proof_value);
|
|
// if !proof_value_matches_msg {
|
|
// return Err(Status::failed_precondition("proof verification"));
|
|
// }
|
|
// TODO: ensure no duplicates somewhere else!
|
|
let liquidity_sources: Vec<LiquiditySource> =
|
|
message.liquidity_sources.into_iter().collect();
|
|
let digests_ciphertexts: BTreeMap<HexBinary, HexBinary> = message.intents;
|
|
let (digests, ciphertexts): (Vec<_>, Vec<_>) = digests_ciphertexts.into_iter().unzip();
|
|
|
|
let sk = self.sk.lock().unwrap();
|
|
let obligations: Vec<SimpleObligation<LiquiditySource, i64, Uuid>> = ciphertexts
|
|
.into_iter()
|
|
.map(|ciphertext| decrypt_obligation(sk.as_ref().unwrap(), &ciphertext))
|
|
.collect();
|
|
|
|
let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default()));
|
|
|
|
let setoffs: Vec<SimpleSetoff<LiquiditySource, i64, Uuid>> = mtcs.run(obligations).unwrap();
|
|
let setoffs_enc: BTreeMap<RawHash, SettleOff> = setoffs
|
|
.into_iter()
|
|
.map(|so| into_settle_offs(so, &liquidity_sources))
|
|
.zip(digests)
|
|
.map(|(settle_off, digest)| (digest, settle_off))
|
|
.collect();
|
|
|
|
let msg = SubmitSetoffsMsg { setoffs_enc };
|
|
println!("setoff_msg: {:?}", msg);
|
|
|
|
let attestation = self
|
|
.attestor
|
|
.attestation(msg.clone())
|
|
.map_err(|e| Status::internal(e.to_string()))?;
|
|
|
|
let attested_msg = RawAttested {
|
|
msg,
|
|
attestation: A::RawAttestation::from(attestation),
|
|
};
|
|
let message = serde_json::to_string(&attested_msg).unwrap();
|
|
Ok(Response::new(RunClearingResponse { message }))
|
|
}
|
|
}
|
|
|
|
// TODO Switch from Vec<_> to Vec<LiquiditySource>
|
|
fn into_settle_offs(
|
|
so: SimpleSetoff<LiquiditySource, i64, Uuid>,
|
|
liquidity_sources: &Vec<LiquiditySource>,
|
|
) -> SettleOff {
|
|
println!("\nsetoff: {:?}", so);
|
|
println!("\nliq sources: {:?}", liquidity_sources);
|
|
|
|
// TODO: temporary patch, fix issue with liquidity sources becoming type External so that .contains() can be called directly
|
|
let liquidity_sources_addrs = liquidity_sources
|
|
.iter()
|
|
.map(|lqs| lqs.address.clone())
|
|
.collect::<Vec<Addr>>();
|
|
|
|
// In tenders and acceptances, the creditor's balance decreases
|
|
if liquidity_sources_addrs.contains(&so.debtor.address)
|
|
|| liquidity_sources_addrs.contains(&so.creditor.address)
|
|
{
|
|
SettleOff::Transfer(Transfer {
|
|
payer: so.creditor.address.clone(),
|
|
payee: so.debtor.address.clone(),
|
|
// TODO: Include denominations
|
|
amount: ("peppicoin".to_owned(), Uint128::from(so.set_off as u128)),
|
|
})
|
|
} else {
|
|
// TODO: Tracked by issue #22
|
|
|
|
// A no-op for the time being.
|
|
SettleOff::SetOff(vec![])
|
|
}
|
|
}
|
|
|
|
fn decrypt_obligation(
|
|
sk: &SigningKey,
|
|
ciphertext: &RawCipherText,
|
|
) -> SimpleObligation<LiquiditySource, i64, Uuid> {
|
|
let o: ContractObligation = {
|
|
let o = decrypt(&sk.to_bytes(), ciphertext).unwrap();
|
|
serde_json::from_slice(&o).unwrap()
|
|
};
|
|
|
|
SimpleObligation::new(
|
|
None,
|
|
LiquiditySource {
|
|
address: o.debtor,
|
|
source_type: LiquiditySourceType::External,
|
|
},
|
|
LiquiditySource {
|
|
address: o.creditor,
|
|
source_type: LiquiditySourceType::External,
|
|
},
|
|
i64::try_from(o.amount).unwrap(),
|
|
)
|
|
.unwrap()
|
|
}
|