Dyn liquidity sources (#52)

This commit is contained in:
Shoaib Ahmed 2024-06-18 02:30:00 -07:00 committed by GitHub
parent d78183b2c8
commit 3509edfe3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 221 additions and 127 deletions

View file

@ -4,7 +4,7 @@ use cosmwasm_std::{
}; };
use cw2::set_contract_version; use cw2::set_contract_version;
use cw20_base::{ use cw20_base::{
contract::{execute_mint, query_balance as cw20_query_balance}, contract::query_balance as cw20_query_balance,
state::{MinterData, TokenInfo, TOKEN_INFO}, state::{MinterData, TokenInfo, TOKEN_INFO},
}; };
use quartz_cw::{handler::RawHandler, state::EPOCH_COUNTER}; use quartz_cw::{handler::RawHandler, state::EPOCH_COUNTER};
@ -12,7 +12,10 @@ use quartz_cw::{handler::RawHandler, state::EPOCH_COUNTER};
use crate::{ use crate::{
error::ContractError, error::ContractError,
msg::{ msg::{
execute::{SubmitObligationMsg, SubmitObligationsMsg, SubmitSetoffsMsg}, execute::{
Cw20Transfer, FaucetMintMsg, SubmitObligationMsg, SubmitObligationsMsg,
SubmitSetoffsMsg,
},
ExecuteMsg, InstantiateMsg, QueryMsg, ExecuteMsg, InstantiateMsg, QueryMsg,
}, },
state::{ state::{
@ -63,35 +66,6 @@ pub fn instantiate(
}; };
TOKEN_INFO.save(deps.storage, &data)?; TOKEN_INFO.save(deps.storage, &data)?;
let info = MessageInfo {
sender: env.contract.address.clone(),
funds: vec![],
};
execute_mint(
deps.branch(),
env.clone(),
info.clone(),
"wasm1qv9nel6lwtrq5jmwruxfndqw7ejskn5ysz53hp".to_owned(),
Uint128::new(1000),
)?;
execute_mint(
deps.branch(),
env.clone(),
info.clone(),
"wasm1tfxrdcj5kk6rewzmmkku4d9htpjqr0kk6lcftv".to_owned(),
Uint128::new(1000),
)?;
execute_mint(
deps.branch(),
env.clone(),
info.clone(),
"wasm1gjg72awjl7jvtmq4kjqp3al9p6crstpar8wgn5".to_owned(),
Uint128::new(1000),
)?;
Ok(Response::new() Ok(Response::new()
.add_attribute("method", "instantiate") .add_attribute("method", "instantiate")
.add_attribute("owner", info.sender)) .add_attribute("owner", info.sender))
@ -106,6 +80,12 @@ pub fn execute(
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
match msg { match msg {
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),
ExecuteMsg::FaucetMint(FaucetMintMsg { recipient, amount }) => {
execute::faucet_mint(deps, env, recipient, amount)
}
ExecuteMsg::Transfer(Cw20Transfer { recipient, amount }) => Ok(
cw20_base::contract::execute_transfer(deps, env, info, recipient, amount.into())?,
),
ExecuteMsg::SubmitObligation(SubmitObligationMsg { ciphertext, digest }) => { ExecuteMsg::SubmitObligation(SubmitObligationMsg { ciphertext, digest }) => {
execute::submit_obligation(deps, ciphertext, digest) execute::submit_obligation(deps, ciphertext, digest)
} }
@ -120,7 +100,7 @@ pub fn execute(
Ok(Response::new()) Ok(Response::new())
} }
ExecuteMsg::SubmitSetoffs(SubmitSetoffsMsg { setoffs_enc }) => { ExecuteMsg::SubmitSetoffs(SubmitSetoffsMsg { setoffs_enc }) => {
execute::submit_setoffs(deps, env, info, setoffs_enc) execute::submit_setoffs(deps, env, setoffs_enc)
} }
ExecuteMsg::InitClearing => execute::init_clearing(deps), ExecuteMsg::InitClearing => execute::init_clearing(deps),
} }
@ -131,6 +111,7 @@ pub mod execute {
use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response, StdResult}; use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response, StdResult};
use cw20_base::contract::{execute_burn, execute_mint}; use cw20_base::contract::{execute_burn, execute_mint};
use k256::ecdsa::VerifyingKey;
use quartz_cw::state::{Hash, EPOCH_COUNTER}; use quartz_cw::state::{Hash, EPOCH_COUNTER};
use crate::{ use crate::{
@ -141,6 +122,28 @@ pub mod execute {
ContractError, ContractError,
}; };
pub fn faucet_mint(
mut deps: DepsMut,
env: Env,
recipient: String,
amount: u64,
) -> Result<Response, ContractError> {
let info = MessageInfo {
sender: env.contract.address.clone(),
funds: vec![],
};
execute_mint(
deps.branch(),
env.clone(),
info.clone(),
recipient.to_string(),
amount.into(),
)?;
Ok(Response::new().add_attribute("action", "faucet_mint"))
}
pub fn submit_obligation( pub fn submit_obligation(
deps: DepsMut, deps: DepsMut,
ciphertext: HexBinary, ciphertext: HexBinary,
@ -167,11 +170,12 @@ pub mod execute {
pub fn append_liquidity_sources( pub fn append_liquidity_sources(
deps: DepsMut, deps: DepsMut,
liquidity_sources: Vec<String>, liquidity_sources: Vec<HexBinary>,
) -> Result<(), ContractError> { ) -> Result<(), ContractError> {
// validate liquidity sources as public keys
liquidity_sources liquidity_sources
.iter() .iter()
.try_for_each(|ls| deps.api.addr_validate(ls).map(|_| ()))?; .try_for_each(|ls| VerifyingKey::from_sec1_bytes(ls).map(|_| ()))?;
// store the liquidity sources // store the liquidity sources
LiquiditySourcesItem::new(&current_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?) LiquiditySourcesItem::new(&current_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?)
@ -186,7 +190,6 @@ pub mod execute {
pub fn submit_setoffs( pub fn submit_setoffs(
mut deps: DepsMut, mut deps: DepsMut,
env: Env, env: Env,
_info: MessageInfo,
setoffs_enc: BTreeMap<RawHash, SettleOff>, setoffs_enc: BTreeMap<RawHash, SettleOff>,
) -> Result<Response, ContractError> { ) -> Result<Response, ContractError> {
// store the `BTreeMap<RawHash, RawCipherText>` // store the `BTreeMap<RawHash, RawCipherText>`

View file

@ -13,6 +13,8 @@ pub struct InstantiateMsg(pub QuartzInstantiateMsg);
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
pub enum ExecuteMsg { pub enum ExecuteMsg {
Quartz(QuartzExecuteMsg), Quartz(QuartzExecuteMsg),
FaucetMint(execute::FaucetMintMsg),
Transfer(execute::Cw20Transfer),
SubmitObligation(execute::SubmitObligationMsg), SubmitObligation(execute::SubmitObligationMsg),
SubmitObligations(execute::SubmitObligationsMsg), SubmitObligations(execute::SubmitObligationsMsg),
SubmitSetoffs(execute::SubmitSetoffsMsg), SubmitSetoffs(execute::SubmitSetoffsMsg),
@ -22,6 +24,18 @@ pub enum ExecuteMsg {
pub mod execute { pub mod execute {
use super::*; use super::*;
#[cw_serde]
pub struct FaucetMintMsg {
pub recipient: String,
pub amount: u64,
}
#[cw_serde]
pub struct Cw20Transfer {
pub recipient: String,
pub amount: u64,
}
#[cw_serde] #[cw_serde]
pub struct SubmitObligationMsg { pub struct SubmitObligationMsg {
pub ciphertext: HexBinary, pub ciphertext: HexBinary,
@ -33,7 +47,7 @@ pub mod execute {
#[cw_serde] #[cw_serde]
pub struct SubmitObligationsMsg { pub struct SubmitObligationsMsg {
pub obligations: Vec<SubmitObligationMsg>, pub obligations: Vec<SubmitObligationMsg>,
pub liquidity_sources: Vec<String>, pub liquidity_sources: Vec<HexBinary>,
} }
#[cw_serde] #[cw_serde]
@ -69,7 +83,7 @@ pub struct GetAllSetoffsResponse {
#[cw_serde] #[cw_serde]
pub struct GetLiquiditySourcesResponse { pub struct GetLiquiditySourcesResponse {
pub liquidity_sources: Vec<String>, pub liquidity_sources: Vec<HexBinary>,
} }
#[cfg(test)] #[cfg(test)]

View file

@ -10,7 +10,7 @@ pub type RawCipherText = HexBinary;
pub type ObligationsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>; pub type ObligationsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>;
pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, SettleOff>>; pub type SetoffsItem<'a> = Item<'a, BTreeMap<RawHash, SettleOff>>;
pub type LiquiditySourcesItem<'a> = Item<'a, BTreeSet<String>>; pub type LiquiditySourcesItem<'a> = Item<'a, BTreeSet<HexBinary>>;
#[cw_serde] #[cw_serde]
pub struct State { pub struct State {

View file

@ -16,6 +16,7 @@ use mtcs::{
algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs, algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs,
obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs, obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs,
}; };
use serde::{Deserialize, Serialize};
use tonic::{Request, Response, Result as TonicResult, Status}; use tonic::{Request, Response, Result as TonicResult, Status};
use crate::{ use crate::{
@ -23,14 +24,18 @@ use crate::{
proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse}, proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse},
}; };
const BANK_PK: &str = "0216254f4636c4e68ae22d98538851a46810b65162fe37bf57cba6d563617c913e";
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MtcsService<A> { pub struct MtcsService<A> {
sk: Arc<Mutex<Option<SigningKey>>>, sk: Arc<Mutex<Option<SigningKey>>>,
_attestor: A, _attestor: A,
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RunClearingMessage {
intents: BTreeMap<RawHash, RawCipherText>,
liquidity_sources: Vec<HexBinary>,
}
impl<A> MtcsService<A> impl<A> MtcsService<A>
where where
A: Attestor, A: Attestor,
@ -49,10 +54,12 @@ where
&self, &self,
request: Request<RunClearingRequest>, request: Request<RunClearingRequest>,
) -> TonicResult<Response<RunClearingResponse>> { ) -> TonicResult<Response<RunClearingResponse>> {
let message: RunClearingMessage = {
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()))?
};
let digests_ciphertexts: BTreeMap<RawHash, RawCipherText> = let digests_ciphertexts = message.intents;
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?;
let (digests, ciphertexts): (Vec<_>, Vec<_>) = digests_ciphertexts.into_iter().unzip(); let (digests, ciphertexts): (Vec<_>, Vec<_>) = digests_ciphertexts.into_iter().unzip();
let sk = self.sk.lock().unwrap(); let sk = self.sk.lock().unwrap();
@ -64,9 +71,16 @@ where
let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default())); let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default()));
let setoffs: Vec<SimpleSetoff<_, i64>> = mtcs.run(obligations).unwrap(); let setoffs: Vec<SimpleSetoff<_, i64>> = mtcs.run(obligations).unwrap();
let liquidity_sources: Vec<_> = message
.liquidity_sources
.into_iter()
.map(|ls| VerifyingKey::from_sec1_bytes(&ls))
.collect::<Result<_, _>>()
.map_err(|e| Status::invalid_argument(e.to_string()))?;
let setoffs_enc: BTreeMap<RawHash, SettleOff> = setoffs let setoffs_enc: BTreeMap<RawHash, SettleOff> = setoffs
.into_iter() .into_iter()
.map(into_settle_offs) .map(|so| into_settle_offs(so, &liquidity_sources))
.zip(digests) .zip(digests)
.map(|(settle_off, digest)| (digest, settle_off)) .map(|(settle_off, digest)| (digest, settle_off))
.collect(); .collect();
@ -76,25 +90,26 @@ where
} }
} }
fn into_settle_offs(so: SimpleSetoff<HexBinary, i64>) -> SettleOff { fn into_settle_offs(
so: SimpleSetoff<HexBinary, i64>,
liquidity_sources: &[VerifyingKey],
) -> SettleOff {
let debtor_pk = VerifyingKey::from_sec1_bytes(&so.debtor).unwrap(); let debtor_pk = VerifyingKey::from_sec1_bytes(&so.debtor).unwrap();
let creditor_pk = VerifyingKey::from_sec1_bytes(&so.creditor).unwrap(); let creditor_pk = VerifyingKey::from_sec1_bytes(&so.creditor).unwrap();
let bank_pk = VerifyingKey::from_sec1_bytes(&hex::decode(BANK_PK).unwrap()).unwrap(); if let Some(ls_pk) = liquidity_sources.iter().find(|ls| ls == &&debtor_pk) {
let bank_addrs = wasm_address(bank_pk);
if debtor_pk == bank_pk {
// A setoff on a tender should result in the creditor's (i.e. the tender receiver) balance // A setoff on a tender should result in the creditor's (i.e. the tender receiver) balance
// decreasing by the setoff amount // decreasing by the setoff amount
SettleOff::Transfer(Transfer { SettleOff::Transfer(Transfer {
payer: wasm_address(creditor_pk), payer: wasm_address(creditor_pk),
payee: bank_addrs, payee: wasm_address(*ls_pk),
amount: so.set_off as u64, amount: so.set_off as u64,
}) })
} else if creditor_pk == bank_pk { } else if let Some(ls_pk) = liquidity_sources.iter().find(|ls| ls == &&creditor_pk) {
// A setoff on an acceptance should result in the debtor's (i.e. the acceptance initiator) // A setoff on an acceptance should result in the debtor's (i.e. the acceptance initiator)
// balance increasing by the setoff amount // balance increasing by the setoff amount
SettleOff::Transfer(Transfer { SettleOff::Transfer(Transfer {
payer: bank_addrs, payer: wasm_address(*ls_pk),
payee: wasm_address(debtor_pk), payee: wasm_address(debtor_pk),
amount: so.set_off as u64, amount: so.set_off as u64,
}) })

View file

@ -6,6 +6,9 @@ use displaydoc::Display;
use reqwest::Url; use reqwest::Url;
use subtle_encoding::{bech32::decode as bech32_decode, Error as Bech32DecodeError}; use subtle_encoding::{bech32::decode as bech32_decode, Error as Bech32DecodeError};
use thiserror::Error; use thiserror::Error;
use uuid::Uuid;
use crate::ADDRESS_PREFIX;
#[derive(Clone, Debug, Parser)] #[derive(Clone, Debug, Parser)]
#[command(author, version, about)] #[command(author, version, about)]
@ -61,9 +64,17 @@ pub enum CliCommand {
/// epoch pk /// epoch pk
#[arg(short, long)] #[arg(short, long)]
epoch_pk: String, epoch_pk: String,
/// liquidity sources' UUIDs
#[arg(short, long, num_args = 1.., value_parser = parse_uuid)]
liquidity_sources: Vec<Uuid>,
}, },
/// Sync set-offs /// Sync set-offs
SyncSetOffs, SyncSetOffs,
/// Get address for Uuid
GetAddress {
#[arg(long, value_parser = parse_uuid)]
uuid: Uuid,
},
} }
#[derive(Display, Error, Debug)] #[derive(Display, Error, Debug)]
@ -76,9 +87,13 @@ pub enum AddressError {
fn wasm_address(address_str: &str) -> Result<AccountId, AddressError> { fn wasm_address(address_str: &str) -> Result<AccountId, AddressError> {
let (hr, _) = bech32_decode(address_str).map_err(AddressError::NotBech32Encoded)?; let (hr, _) = bech32_decode(address_str).map_err(AddressError::NotBech32Encoded)?;
if hr != "wasm" { if hr != ADDRESS_PREFIX {
return Err(AddressError::HumanReadableMismatch(hr)); return Err(AddressError::HumanReadableMismatch(hr));
} }
Ok(address_str.parse().unwrap()) Ok(address_str.parse().unwrap())
} }
fn parse_uuid(uuid_str: &str) -> Result<Uuid, String> {
Uuid::parse_str(uuid_str).map_err(|e| e.to_string())
}

View file

@ -12,9 +12,10 @@ use bip32::{
ecdsa::VerifyingKey, ecdsa::VerifyingKey,
sha2::{Digest, Sha256}, sha2::{Digest, Sha256},
}, },
Language, Mnemonic, Prefix, PrivateKey, Seed, XPrv, Error as Bip32Error, Language, Mnemonic, Prefix, PrivateKey, Seed, XPrv,
}; };
use clap::Parser; use clap::Parser;
use cosmrs::{tendermint::account::Id as TmAccountId, AccountId};
use cosmwasm_std::HexBinary; use cosmwasm_std::HexBinary;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
@ -26,7 +27,7 @@ use crate::{
obligato_client::{http::HttpClient, Client}, obligato_client::{http::HttpClient, Client},
types::{ types::{
Obligation, ObligatoObligation, ObligatoSetOff, RawEncryptedObligation, RawObligation, Obligation, ObligatoObligation, ObligatoSetOff, RawEncryptedObligation, RawObligation,
RawOffset, RawSetOff, SubmitObligationsMsg, RawOffset, RawSetOff, SubmitObligationsMsg, SubmitObligationsMsgInner,
}, },
wasmd_client::{CliWasmdClient, QueryResult, WasmdClient}, wasmd_client::{CliWasmdClient, QueryResult, WasmdClient},
}; };
@ -38,8 +39,7 @@ mod wasmd_client;
const MNEMONIC_PHRASE: &str = "clutch debate vintage foster barely primary clown leader sell manual leopard ladder wet must embody story oyster imitate cable alien six square rice wedding"; const MNEMONIC_PHRASE: &str = "clutch debate vintage foster barely primary clown leader sell manual leopard ladder wet must embody story oyster imitate cable alien six square rice wedding";
const ALICE_ID: &str = "7bfad4e8-d898-4ce2-bbac-1beff7182319"; const ADDRESS_PREFIX: &str = "wasm";
const BANK_DEBTOR_ID: &str = "3879fa15-d86e-4464-b679-0a3d78cf3dd3";
type Sha256Digest = [u8; 32]; type Sha256Digest = [u8; 32];
@ -65,15 +65,38 @@ async fn main() -> Result<(), DynError> {
.init(); .init();
match cli.command { match cli.command {
CliCommand::SyncObligations { ref epoch_pk } => { CliCommand::SyncObligations {
sync_obligations(cli.clone(), epoch_pk).await? ref epoch_pk,
} ref liquidity_sources,
} => sync_obligations(cli.clone(), epoch_pk, liquidity_sources).await?,
CliCommand::SyncSetOffs => sync_setoffs(cli).await?, CliCommand::SyncSetOffs => sync_setoffs(cli).await?,
CliCommand::GetAddress { uuid } => address_from_uuid(uuid)?,
} }
Ok(()) Ok(())
} }
fn address_from_uuid(uuid: Uuid) -> Result<(), DynError> {
let seed = global_seed()?;
let sk = derive_child_xprv(&seed, uuid);
let pk_b = sk.public_key().public_key().to_sec1_bytes();
let pk = VerifyingKey::from_sec1_bytes(&pk_b)?;
println!("{}", wasm_address(pk));
Ok(())
}
fn wasm_address(pk: VerifyingKey) -> String {
let tm_pk = TmAccountId::from(pk);
AccountId::new(ADDRESS_PREFIX, tm_pk.as_bytes())
.unwrap()
.to_string()
}
fn global_seed() -> Result<Seed, Bip32Error> {
let mnemonic = Mnemonic::new(MNEMONIC_PHRASE, Language::English)?;
Ok(mnemonic.to_seed("password"))
}
async fn sync_setoffs(cli: Cli) -> Result<(), DynError> { async fn sync_setoffs(cli: Cli) -> Result<(), DynError> {
let wasmd_client = CliWasmdClient::new(cli.node); let wasmd_client = CliWasmdClient::new(cli.node);
let query_result: QueryResult<QueryAllSetoffsResponse> = let query_result: QueryResult<QueryAllSetoffsResponse> =
@ -123,34 +146,48 @@ async fn sync_setoffs(cli: Cli) -> Result<(), DynError> {
Ok(()) Ok(())
} }
async fn sync_obligations(cli: Cli, epoch_pk: &str) -> Result<(), DynError> { async fn sync_obligations(
cli: Cli,
epoch_pk: &str,
liquidity_sources: &[Uuid],
) -> Result<(), DynError> {
let mut intents = { let mut intents = {
let client = HttpClient::new(cli.obligato_url, cli.obligato_key); let client = HttpClient::new(cli.obligato_url.clone(), cli.obligato_key);
client.get_obligations().await.unwrap() client
.get_obligations()
.await
.map_err(|_| cli.obligato_url.to_string())?
}; };
let bank_id = Uuid::parse_str(BANK_DEBTOR_ID).unwrap(); let keys = derive_keys(&mut intents, liquidity_sources)?;
let keys = derive_keys(&mut intents, bank_id)?;
write_keys_to_file(cli.keys_file, &keys); write_keys_to_file(cli.keys_file, &keys);
add_default_acceptances(&mut intents, bank_id); add_default_acceptances(&mut intents, liquidity_sources);
debug!("intents: {intents:?}"); debug!("intents: {intents:?}");
let intents_enc = { let intents_enc = {
let epoch_pk = VerifyingKey::from_sec1_bytes(&hex::decode(epoch_pk).unwrap()).unwrap(); let epoch_pk = VerifyingKey::from_sec1_bytes(&hex::decode(epoch_pk).unwrap()).unwrap();
encrypt_intents(intents, keys, &epoch_pk, cli.obligation_user_map_file) encrypt_intents(intents, &keys, &epoch_pk, cli.obligation_user_map_file)
}; };
debug!("Encrypted {} intents", intents_enc.len()); debug!("Encrypted {} intents", intents_enc.len());
let msg = create_wasm_msg(intents_enc); let liquidity_sources = liquidity_sources
.iter()
.map(|id| keys[id].private_key().public_key())
.collect();
let msg = create_wasm_msg(intents_enc, liquidity_sources)?;
let wasmd_client = CliWasmdClient::new(cli.node); let wasmd_client = CliWasmdClient::new(cli.node);
wasmd_client.tx_execute(&cli.contract, &cli.chain_id, 3000000, cli.user, msg)?; wasmd_client.tx_execute(&cli.contract, &cli.chain_id, 3000000, cli.user, msg)?;
Ok(()) Ok(())
} }
fn create_wasm_msg(obligations_enc: Vec<(Sha256Digest, Vec<u8>)>) -> serde_json::Value { fn create_wasm_msg(
obligations_enc: Vec<(Sha256Digest, Vec<u8>)>,
liquidity_sources: Vec<VerifyingKey>,
) -> Result<serde_json::Value, DynError> {
let obligations_enc: Vec<_> = obligations_enc let obligations_enc: Vec<_> = obligations_enc
.into_iter() .into_iter()
.map(|(digest, ciphertext)| { .map(|(digest, ciphertext)| {
@ -160,15 +197,23 @@ fn create_wasm_msg(obligations_enc: Vec<(Sha256Digest, Vec<u8>)>) -> serde_json:
}) })
.collect(); .collect();
let liquidity_sources = liquidity_sources
.into_iter()
.map(|pk| HexBinary::from(pk.to_sec1_bytes().as_ref()))
.collect();
let msg = SubmitObligationsMsg { let msg = SubmitObligationsMsg {
submit_obligations: obligations_enc, submit_obligations: SubmitObligationsMsgInner {
obligations: obligations_enc,
liquidity_sources,
},
}; };
serde_json::to_value(msg).unwrap() serde_json::to_value(msg).map_err(Into::into)
} }
fn encrypt_intents( fn encrypt_intents(
intents: Vec<ObligatoObligation>, intents: Vec<ObligatoObligation>,
keys: HashMap<Uuid, XPrv>, keys: &HashMap<Uuid, XPrv>,
epoch_pk: &VerifyingKey, epoch_pk: &VerifyingKey,
obligation_user_map_file: PathBuf, obligation_user_map_file: PathBuf,
) -> Vec<(Sha256Digest, Vec<u8>)> { ) -> Vec<(Sha256Digest, Vec<u8>)> {
@ -209,17 +254,19 @@ fn encrypt_intents(
intents_enc intents_enc
} }
fn add_default_acceptances(obligations: &mut Vec<ObligatoObligation>, bank_id: Uuid) { fn add_default_acceptances(obligations: &mut Vec<ObligatoObligation>, liquidity_sources: &[Uuid]) {
let acceptances = obligations.iter().fold(HashSet::new(), |mut acc, o| { let acceptances = obligations.iter().fold(HashSet::new(), |mut acc, o| {
if o.debtor_id != bank_id { if !liquidity_sources.contains(&o.debtor_id) {
for ls in liquidity_sources {
let acceptance = ObligatoObligation { let acceptance = ObligatoObligation {
id: Default::default(), id: Default::default(),
debtor_id: o.creditor_id, debtor_id: o.creditor_id,
creditor_id: bank_id, creditor_id: *ls,
amount: u32::MAX as u64, amount: u32::MAX as u64,
}; };
acc.insert(acceptance); acc.insert(acceptance);
} }
}
acc acc
}); });
@ -274,41 +321,46 @@ fn write_obligation_user_map_to_file(
fn derive_keys( fn derive_keys(
obligations: &mut Vec<ObligatoObligation>, obligations: &mut Vec<ObligatoObligation>,
bank_id: Uuid, liquidity_sources: &[Uuid],
) -> Result<HashMap<Uuid, XPrv>, DynError> { ) -> Result<HashMap<Uuid, XPrv>, DynError> {
// Derive a BIP39 seed value using the given password // Derive a BIP39 seed value using the given password
let seed = { let seed = global_seed()?;
let mnemonic = Mnemonic::new(MNEMONIC_PHRASE, Language::English)?;
mnemonic.to_seed("password")
};
obligations.sort_by_key(|o| o.debtor_id); obligations.sort_by_key(|o| o.debtor_id);
let mut keys = HashMap::new(); let mut keys = HashMap::new();
let mut child_num = 0;
let alice_id = Uuid::parse_str(ALICE_ID).unwrap(); for ls in liquidity_sources {
keys.entry(*ls)
keys.entry(alice_id) .or_insert_with(|| derive_child_xprv(&seed, *ls));
.or_insert_with(|| derive_child_xprv(&seed, &mut child_num)); }
keys.entry(bank_id)
.or_insert_with(|| derive_child_xprv(&seed, &mut child_num));
for o in obligations { for o in obligations {
keys.entry(o.debtor_id) keys.entry(o.debtor_id)
.or_insert_with(|| derive_child_xprv(&seed, &mut child_num)); .or_insert_with(|| derive_child_xprv(&seed, o.debtor_id));
keys.entry(o.creditor_id) keys.entry(o.creditor_id)
.or_insert_with(|| derive_child_xprv(&seed, &mut child_num)); .or_insert_with(|| derive_child_xprv(&seed, o.creditor_id));
} }
Ok(keys) Ok(keys)
} }
fn derive_child_xprv(seed: &Seed, i: &mut usize) -> XPrv { fn derive_child_xprv(seed: &Seed, uuid: Uuid) -> XPrv {
let child_path = format!("m/0/44'/118'/0'/0/{}", i).parse().unwrap(); // Hash the UUID using SHA-256
let mut hasher = Sha256::new();
hasher.update(uuid.as_bytes());
let uuid_digest = hasher.finalize();
// Convert the hash bytes to a number
let uuid_digest_num = u128::from_be_bytes(uuid_digest[..16].try_into().unwrap());
// Take modulo (2^31 - 1)
let address_index = uuid_digest_num % ((1u128 << 31) - 1);
let child_path = format!("m/0/44'/118'/0'/0/{address_index}")
.parse()
.unwrap();
let child_xprv = XPrv::derive_from_path(seed, &child_path); let child_xprv = XPrv::derive_from_path(seed, &child_path);
*i += 1;
child_xprv.unwrap() child_xprv.unwrap()
} }
@ -316,52 +368,40 @@ fn derive_child_xprv(seed: &Seed, i: &mut usize) -> XPrv {
mod tests { mod tests {
use std::{error::Error, str::FromStr}; use std::{error::Error, str::FromStr};
use bip32::{Language, Mnemonic, Prefix, PrivateKey, XPrv}; use bip32::{Mnemonic, Prefix, PrivateKey, XPrv};
use rand_core::OsRng; use rand_core::OsRng;
use uuid::Uuid;
use crate::{derive_child_xprv, MNEMONIC_PHRASE}; use crate::{derive_child_xprv, global_seed};
#[test] #[test]
fn test_create_mnemonic() { fn test_create_mnemonic() {
// Generate random Mnemonic using the default language (English) // Generate random Mnemonic using the default language (English)
let mnemonic = Mnemonic::random(&mut OsRng, Default::default()); let mnemonic = Mnemonic::random(&mut OsRng, Default::default());
println!("{}", mnemonic.phrase()); println!("{}", mnemonic.phrase());
} }
#[test] #[test]
fn test_enc_dec_for_derived() -> Result<(), Box<dyn Error>> { fn test_enc_dec_for_derived() -> Result<(), Box<dyn Error>> {
let seed = { let seed = global_seed()?;
let mnemonic = Mnemonic::new(MNEMONIC_PHRASE, Language::English)?;
mnemonic.to_seed("password") let alice_uuid = Uuid::from_u128(1);
}; let alice_sk = derive_child_xprv(&seed, alice_uuid);
let alice_pk = alice_sk.private_key().public_key();
let mut child_num = 0;
let alice_sk = derive_child_xprv(&seed, &mut child_num);
let alice_sk_str = alice_sk.to_string(Prefix::XPRV).to_string();
assert_eq!( assert_eq!(
alice_sk.private_key().public_key().to_sec1_bytes(), alice_pk.to_sec1_bytes(),
hex::decode("02027e3510f66f1f6c1ea5e3600062255928e518220f7883810cac3fc7fc092057") hex::decode("0219b0b8ee5fe9b317b69119fd15170d79737380c4f020e251b7839096f5513ccf")
.unwrap() .unwrap()
.into() .into()
); );
let alice_sk_str = alice_sk.to_string(Prefix::XPRV).to_string();
assert_eq!(XPrv::from_str(&alice_sk_str).unwrap(), alice_sk); assert_eq!(XPrv::from_str(&alice_sk_str).unwrap(), alice_sk);
let alice_pk = alice_sk.private_key().public_key();
assert_eq!(
alice_pk.to_sec1_bytes().into_vec(),
vec![
2, 2, 126, 53, 16, 246, 111, 31, 108, 30, 165, 227, 96, 0, 98, 37, 89, 40, 229, 24,
34, 15, 120, 131, 129, 12, 172, 63, 199, 252, 9, 32, 87
]
);
let msg = r#"{"debtor":"02027e3510f66f1f6c1ea5e3600062255928e518220f7883810cac3fc7fc092057","creditor":"0216254f4636c4e68ae22d98538851a46810b65162fe37bf57cba6d563617c913e","amount":10,"salt":"65c188bcc133add598f7eecc449112f4bf61024345316cff0eb5ce61291991b141073dcd3c543ea142e66fffa8f483dc382043d37e490ef9b8069c489ce94a0b"}"#; let msg = r#"{"debtor":"02027e3510f66f1f6c1ea5e3600062255928e518220f7883810cac3fc7fc092057","creditor":"0216254f4636c4e68ae22d98538851a46810b65162fe37bf57cba6d563617c913e","amount":10,"salt":"65c188bcc133add598f7eecc449112f4bf61024345316cff0eb5ce61291991b141073dcd3c543ea142e66fffa8f483dc382043d37e490ef9b8069c489ce94a0b"}"#;
let ciphertext = ecies::encrypt(&alice_pk.to_sec1_bytes(), msg.as_bytes()).unwrap(); let ciphertext = ecies::encrypt(&alice_pk.to_sec1_bytes(), msg.as_bytes()).unwrap();
// let ciphertext = hex::decode("0418d9051cbfc86c8ddd57ae43ea3d1ac8b30353a3ecd8c806bb11f0693dfd282d5f07d1de32cbcd933d5ab7cd0aa171c972e75531b915e968f0fdeba78fa3f359c7f3ef7ae2dfffeb19493e9b2418dc774e6e80448a2dc4a7ba657cd4a8456e120977ebe372a57187d53981cc5856fbd63e9c1bdf001ed71c3d50cbaff594561191d33dad852cb782126f480add2cc92758b59eb63de857d299eaa5f09fbc55643a73b1d8206ce83453b5296b566d9f622520679bb3e6d9c8b7a707f33d3093c41dfc0a8267749b4028e9ee0faad0c8df64f1682a348f220585fdd9b9ac411bdaaa6a249b45accc89a80e5af09abb239231aa869e29459e562721b685d98b3da3eeaef14e1c5f3bd20cf27c0cbbae7b5c618e737df9a84f9a040bb472b7254af2cf4ccc76784cf8432080e528f700ca2a082b7020d94f0f5325dd4998c03972a0b39e6670b65be89e7a80aad7af08a393fcf2e103999254380c1f0355d97ddcdfaeed4bcfaf15b578cee1f6d3fd4ceccd85760b9bd714f81698ddf6fbbc06152a9306a5dd0052c722e390470f0c70eeac81a5da0090").unwrap(); // println!("{}", hex::encode(&ciphertext));
println!("{}", hex::encode(&ciphertext));
let msg_dec = let msg_dec =
ecies::decrypt(&alice_sk.private_key().to_bytes(), ciphertext.as_slice()).unwrap(); ecies::decrypt(&alice_sk.private_key().to_bytes(), ciphertext.as_slice()).unwrap();

View file

@ -4,10 +4,11 @@ use uuid::Uuid;
use crate::{ use crate::{
obligato_client::Client, obligato_client::Client,
types::{ObligatoObligation, ObligatoSetOff}, types::{ObligatoObligation, ObligatoSetOff},
BANK_DEBTOR_ID,
}; };
pub struct MockClient; pub struct MockClient {
pub bank: Uuid,
}
#[async_trait] #[async_trait]
impl Client for MockClient { impl Client for MockClient {
@ -25,7 +26,7 @@ impl Client for MockClient {
// tender: $ --10--> 1 // tender: $ --10--> 1
ObligatoObligation { ObligatoObligation {
id: Uuid::from_u128(2), id: Uuid::from_u128(2),
debtor_id: Uuid::parse_str(BANK_DEBTOR_ID).unwrap(), debtor_id: self.bank,
creditor_id: Uuid::from_u128(1), creditor_id: Uuid::from_u128(1),
amount: 10, amount: 10,
}, },

View file

@ -47,7 +47,13 @@ pub struct RawEncryptedObligation {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SubmitObligationsMsg { pub struct SubmitObligationsMsg {
pub submit_obligations: Vec<RawEncryptedObligation>, pub submit_obligations: SubmitObligationsMsgInner,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SubmitObligationsMsgInner {
pub obligations: Vec<RawEncryptedObligation>,
pub liquidity_sources: Vec<HexBinary>,
} }
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]