Dyn liquidity sources (#52)
This commit is contained in:
parent
d78183b2c8
commit
3509edfe3d
8 changed files with 221 additions and 127 deletions
|
@ -4,7 +4,7 @@ use cosmwasm_std::{
|
|||
};
|
||||
use cw2::set_contract_version;
|
||||
use cw20_base::{
|
||||
contract::{execute_mint, query_balance as cw20_query_balance},
|
||||
contract::query_balance as cw20_query_balance,
|
||||
state::{MinterData, TokenInfo, TOKEN_INFO},
|
||||
};
|
||||
use quartz_cw::{handler::RawHandler, state::EPOCH_COUNTER};
|
||||
|
@ -12,7 +12,10 @@ use quartz_cw::{handler::RawHandler, state::EPOCH_COUNTER};
|
|||
use crate::{
|
||||
error::ContractError,
|
||||
msg::{
|
||||
execute::{SubmitObligationMsg, SubmitObligationsMsg, SubmitSetoffsMsg},
|
||||
execute::{
|
||||
Cw20Transfer, FaucetMintMsg, SubmitObligationMsg, SubmitObligationsMsg,
|
||||
SubmitSetoffsMsg,
|
||||
},
|
||||
ExecuteMsg, InstantiateMsg, QueryMsg,
|
||||
},
|
||||
state::{
|
||||
|
@ -63,35 +66,6 @@ pub fn instantiate(
|
|||
};
|
||||
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()
|
||||
.add_attribute("method", "instantiate")
|
||||
.add_attribute("owner", info.sender))
|
||||
|
@ -106,6 +80,12 @@ pub fn execute(
|
|||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
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 }) => {
|
||||
execute::submit_obligation(deps, ciphertext, digest)
|
||||
}
|
||||
|
@ -120,7 +100,7 @@ pub fn execute(
|
|||
Ok(Response::new())
|
||||
}
|
||||
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),
|
||||
}
|
||||
|
@ -131,6 +111,7 @@ pub mod execute {
|
|||
|
||||
use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response, StdResult};
|
||||
use cw20_base::contract::{execute_burn, execute_mint};
|
||||
use k256::ecdsa::VerifyingKey;
|
||||
use quartz_cw::state::{Hash, EPOCH_COUNTER};
|
||||
|
||||
use crate::{
|
||||
|
@ -141,6 +122,28 @@ pub mod execute {
|
|||
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(
|
||||
deps: DepsMut,
|
||||
ciphertext: HexBinary,
|
||||
|
@ -167,11 +170,12 @@ pub mod execute {
|
|||
|
||||
pub fn append_liquidity_sources(
|
||||
deps: DepsMut,
|
||||
liquidity_sources: Vec<String>,
|
||||
liquidity_sources: Vec<HexBinary>,
|
||||
) -> Result<(), ContractError> {
|
||||
// validate liquidity sources as public keys
|
||||
liquidity_sources
|
||||
.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
|
||||
LiquiditySourcesItem::new(¤t_epoch_key(LIQUIDITY_SOURCES_KEY, deps.storage)?)
|
||||
|
@ -186,7 +190,6 @@ pub mod execute {
|
|||
pub fn submit_setoffs(
|
||||
mut deps: DepsMut,
|
||||
env: Env,
|
||||
_info: MessageInfo,
|
||||
setoffs_enc: BTreeMap<RawHash, SettleOff>,
|
||||
) -> Result<Response, ContractError> {
|
||||
// store the `BTreeMap<RawHash, RawCipherText>`
|
||||
|
|
|
@ -13,6 +13,8 @@ pub struct InstantiateMsg(pub QuartzInstantiateMsg);
|
|||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ExecuteMsg {
|
||||
Quartz(QuartzExecuteMsg),
|
||||
FaucetMint(execute::FaucetMintMsg),
|
||||
Transfer(execute::Cw20Transfer),
|
||||
SubmitObligation(execute::SubmitObligationMsg),
|
||||
SubmitObligations(execute::SubmitObligationsMsg),
|
||||
SubmitSetoffs(execute::SubmitSetoffsMsg),
|
||||
|
@ -22,6 +24,18 @@ pub enum ExecuteMsg {
|
|||
pub mod execute {
|
||||
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]
|
||||
pub struct SubmitObligationMsg {
|
||||
pub ciphertext: HexBinary,
|
||||
|
@ -33,7 +47,7 @@ pub mod execute {
|
|||
#[cw_serde]
|
||||
pub struct SubmitObligationsMsg {
|
||||
pub obligations: Vec<SubmitObligationMsg>,
|
||||
pub liquidity_sources: Vec<String>,
|
||||
pub liquidity_sources: Vec<HexBinary>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
|
@ -69,7 +83,7 @@ pub struct GetAllSetoffsResponse {
|
|||
|
||||
#[cw_serde]
|
||||
pub struct GetLiquiditySourcesResponse {
|
||||
pub liquidity_sources: Vec<String>,
|
||||
pub liquidity_sources: Vec<HexBinary>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -10,7 +10,7 @@ pub type RawCipherText = HexBinary;
|
|||
|
||||
pub type ObligationsItem<'a> = Item<'a, BTreeMap<RawHash, RawCipherText>>;
|
||||
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]
|
||||
pub struct State {
|
||||
|
|
|
@ -16,6 +16,7 @@ use mtcs::{
|
|||
algo::mcmf::primal_dual::PrimalDual, impls::complex_id::ComplexIdMtcs,
|
||||
obligation::SimpleObligation, prelude::DefaultMtcs, setoff::SimpleSetoff, Mtcs,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tonic::{Request, Response, Result as TonicResult, Status};
|
||||
|
||||
use crate::{
|
||||
|
@ -23,14 +24,18 @@ use crate::{
|
|||
proto::{clearing_server::Clearing, RunClearingRequest, RunClearingResponse},
|
||||
};
|
||||
|
||||
const BANK_PK: &str = "0216254f4636c4e68ae22d98538851a46810b65162fe37bf57cba6d563617c913e";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MtcsService<A> {
|
||||
sk: Arc<Mutex<Option<SigningKey>>>,
|
||||
_attestor: A,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RunClearingMessage {
|
||||
intents: BTreeMap<RawHash, RawCipherText>,
|
||||
liquidity_sources: Vec<HexBinary>,
|
||||
}
|
||||
|
||||
impl<A> MtcsService<A>
|
||||
where
|
||||
A: Attestor,
|
||||
|
@ -49,10 +54,12 @@ where
|
|||
&self,
|
||||
request: Request<RunClearingRequest>,
|
||||
) -> TonicResult<Response<RunClearingResponse>> {
|
||||
let message: RunClearingMessage = {
|
||||
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> =
|
||||
serde_json::from_str(&message).map_err(|e| Status::invalid_argument(e.to_string()))?;
|
||||
let digests_ciphertexts = message.intents;
|
||||
let (digests, ciphertexts): (Vec<_>, Vec<_>) = digests_ciphertexts.into_iter().unzip();
|
||||
|
||||
let sk = self.sk.lock().unwrap();
|
||||
|
@ -64,9 +71,16 @@ where
|
|||
let mut mtcs = ComplexIdMtcs::wrapping(DefaultMtcs::new(PrimalDual::default()));
|
||||
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
|
||||
.into_iter()
|
||||
.map(into_settle_offs)
|
||||
.map(|so| into_settle_offs(so, &liquidity_sources))
|
||||
.zip(digests)
|
||||
.map(|(settle_off, digest)| (digest, settle_off))
|
||||
.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 creditor_pk = VerifyingKey::from_sec1_bytes(&so.creditor).unwrap();
|
||||
|
||||
let bank_pk = VerifyingKey::from_sec1_bytes(&hex::decode(BANK_PK).unwrap()).unwrap();
|
||||
let bank_addrs = wasm_address(bank_pk);
|
||||
if debtor_pk == bank_pk {
|
||||
if let Some(ls_pk) = liquidity_sources.iter().find(|ls| ls == &&debtor_pk) {
|
||||
// A setoff on a tender should result in the creditor's (i.e. the tender receiver) balance
|
||||
// decreasing by the setoff amount
|
||||
SettleOff::Transfer(Transfer {
|
||||
payer: wasm_address(creditor_pk),
|
||||
payee: bank_addrs,
|
||||
payee: wasm_address(*ls_pk),
|
||||
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)
|
||||
// balance increasing by the setoff amount
|
||||
SettleOff::Transfer(Transfer {
|
||||
payer: bank_addrs,
|
||||
payer: wasm_address(*ls_pk),
|
||||
payee: wasm_address(debtor_pk),
|
||||
amount: so.set_off as u64,
|
||||
})
|
||||
|
|
|
@ -6,6 +6,9 @@ use displaydoc::Display;
|
|||
use reqwest::Url;
|
||||
use subtle_encoding::{bech32::decode as bech32_decode, Error as Bech32DecodeError};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::ADDRESS_PREFIX;
|
||||
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[command(author, version, about)]
|
||||
|
@ -61,9 +64,17 @@ pub enum CliCommand {
|
|||
/// epoch pk
|
||||
#[arg(short, long)]
|
||||
epoch_pk: String,
|
||||
/// liquidity sources' UUIDs
|
||||
#[arg(short, long, num_args = 1.., value_parser = parse_uuid)]
|
||||
liquidity_sources: Vec<Uuid>,
|
||||
},
|
||||
/// Sync set-offs
|
||||
SyncSetOffs,
|
||||
/// Get address for Uuid
|
||||
GetAddress {
|
||||
#[arg(long, value_parser = parse_uuid)]
|
||||
uuid: Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Display, Error, Debug)]
|
||||
|
@ -76,9 +87,13 @@ pub enum AddressError {
|
|||
|
||||
fn wasm_address(address_str: &str) -> Result<AccountId, AddressError> {
|
||||
let (hr, _) = bech32_decode(address_str).map_err(AddressError::NotBech32Encoded)?;
|
||||
if hr != "wasm" {
|
||||
if hr != ADDRESS_PREFIX {
|
||||
return Err(AddressError::HumanReadableMismatch(hr));
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ use bip32::{
|
|||
ecdsa::VerifyingKey,
|
||||
sha2::{Digest, Sha256},
|
||||
},
|
||||
Language, Mnemonic, Prefix, PrivateKey, Seed, XPrv,
|
||||
Error as Bip32Error, Language, Mnemonic, Prefix, PrivateKey, Seed, XPrv,
|
||||
};
|
||||
use clap::Parser;
|
||||
use cosmrs::{tendermint::account::Id as TmAccountId, AccountId};
|
||||
use cosmwasm_std::HexBinary;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
@ -26,7 +27,7 @@ use crate::{
|
|||
obligato_client::{http::HttpClient, Client},
|
||||
types::{
|
||||
Obligation, ObligatoObligation, ObligatoSetOff, RawEncryptedObligation, RawObligation,
|
||||
RawOffset, RawSetOff, SubmitObligationsMsg,
|
||||
RawOffset, RawSetOff, SubmitObligationsMsg, SubmitObligationsMsgInner,
|
||||
},
|
||||
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 ALICE_ID: &str = "7bfad4e8-d898-4ce2-bbac-1beff7182319";
|
||||
const BANK_DEBTOR_ID: &str = "3879fa15-d86e-4464-b679-0a3d78cf3dd3";
|
||||
const ADDRESS_PREFIX: &str = "wasm";
|
||||
|
||||
type Sha256Digest = [u8; 32];
|
||||
|
||||
|
@ -65,15 +65,38 @@ async fn main() -> Result<(), DynError> {
|
|||
.init();
|
||||
|
||||
match cli.command {
|
||||
CliCommand::SyncObligations { ref epoch_pk } => {
|
||||
sync_obligations(cli.clone(), epoch_pk).await?
|
||||
}
|
||||
CliCommand::SyncObligations {
|
||||
ref epoch_pk,
|
||||
ref liquidity_sources,
|
||||
} => sync_obligations(cli.clone(), epoch_pk, liquidity_sources).await?,
|
||||
CliCommand::SyncSetOffs => sync_setoffs(cli).await?,
|
||||
CliCommand::GetAddress { uuid } => address_from_uuid(uuid)?,
|
||||
}
|
||||
|
||||
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> {
|
||||
let wasmd_client = CliWasmdClient::new(cli.node);
|
||||
let query_result: QueryResult<QueryAllSetoffsResponse> =
|
||||
|
@ -123,34 +146,48 @@ async fn sync_setoffs(cli: Cli) -> Result<(), DynError> {
|
|||
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 client = HttpClient::new(cli.obligato_url, cli.obligato_key);
|
||||
client.get_obligations().await.unwrap()
|
||||
let client = HttpClient::new(cli.obligato_url.clone(), cli.obligato_key);
|
||||
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, bank_id)?;
|
||||
let keys = derive_keys(&mut intents, liquidity_sources)?;
|
||||
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:?}");
|
||||
|
||||
let intents_enc = {
|
||||
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());
|
||||
|
||||
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);
|
||||
wasmd_client.tx_execute(&cli.contract, &cli.chain_id, 3000000, cli.user, msg)?;
|
||||
|
||||
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
|
||||
.into_iter()
|
||||
.map(|(digest, ciphertext)| {
|
||||
|
@ -160,15 +197,23 @@ fn create_wasm_msg(obligations_enc: Vec<(Sha256Digest, Vec<u8>)>) -> serde_json:
|
|||
})
|
||||
.collect();
|
||||
|
||||
let liquidity_sources = liquidity_sources
|
||||
.into_iter()
|
||||
.map(|pk| HexBinary::from(pk.to_sec1_bytes().as_ref()))
|
||||
.collect();
|
||||
|
||||
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(
|
||||
intents: Vec<ObligatoObligation>,
|
||||
keys: HashMap<Uuid, XPrv>,
|
||||
keys: &HashMap<Uuid, XPrv>,
|
||||
epoch_pk: &VerifyingKey,
|
||||
obligation_user_map_file: PathBuf,
|
||||
) -> Vec<(Sha256Digest, Vec<u8>)> {
|
||||
|
@ -209,17 +254,19 @@ fn encrypt_intents(
|
|||
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| {
|
||||
if o.debtor_id != bank_id {
|
||||
if !liquidity_sources.contains(&o.debtor_id) {
|
||||
for ls in liquidity_sources {
|
||||
let acceptance = ObligatoObligation {
|
||||
id: Default::default(),
|
||||
debtor_id: o.creditor_id,
|
||||
creditor_id: bank_id,
|
||||
creditor_id: *ls,
|
||||
amount: u32::MAX as u64,
|
||||
};
|
||||
acc.insert(acceptance);
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
|
||||
|
@ -274,41 +321,46 @@ fn write_obligation_user_map_to_file(
|
|||
|
||||
fn derive_keys(
|
||||
obligations: &mut Vec<ObligatoObligation>,
|
||||
bank_id: Uuid,
|
||||
liquidity_sources: &[Uuid],
|
||||
) -> Result<HashMap<Uuid, XPrv>, DynError> {
|
||||
// Derive a BIP39 seed value using the given password
|
||||
let seed = {
|
||||
let mnemonic = Mnemonic::new(MNEMONIC_PHRASE, Language::English)?;
|
||||
mnemonic.to_seed("password")
|
||||
};
|
||||
let seed = global_seed()?;
|
||||
|
||||
obligations.sort_by_key(|o| o.debtor_id);
|
||||
|
||||
let mut keys = HashMap::new();
|
||||
let mut child_num = 0;
|
||||
|
||||
let alice_id = Uuid::parse_str(ALICE_ID).unwrap();
|
||||
|
||||
keys.entry(alice_id)
|
||||
.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 ls in liquidity_sources {
|
||||
keys.entry(*ls)
|
||||
.or_insert_with(|| derive_child_xprv(&seed, *ls));
|
||||
}
|
||||
|
||||
for o in obligations {
|
||||
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)
|
||||
.or_insert_with(|| derive_child_xprv(&seed, &mut child_num));
|
||||
.or_insert_with(|| derive_child_xprv(&seed, o.creditor_id));
|
||||
}
|
||||
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
fn derive_child_xprv(seed: &Seed, i: &mut usize) -> XPrv {
|
||||
let child_path = format!("m/0/44'/118'/0'/0/{}", i).parse().unwrap();
|
||||
fn derive_child_xprv(seed: &Seed, uuid: Uuid) -> XPrv {
|
||||
// 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);
|
||||
*i += 1;
|
||||
child_xprv.unwrap()
|
||||
}
|
||||
|
||||
|
@ -316,52 +368,40 @@ fn derive_child_xprv(seed: &Seed, i: &mut usize) -> XPrv {
|
|||
mod tests {
|
||||
use std::{error::Error, str::FromStr};
|
||||
|
||||
use bip32::{Language, Mnemonic, Prefix, PrivateKey, XPrv};
|
||||
use bip32::{Mnemonic, Prefix, PrivateKey, XPrv};
|
||||
use rand_core::OsRng;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{derive_child_xprv, MNEMONIC_PHRASE};
|
||||
use crate::{derive_child_xprv, global_seed};
|
||||
|
||||
#[test]
|
||||
fn test_create_mnemonic() {
|
||||
// Generate random Mnemonic using the default language (English)
|
||||
let mnemonic = Mnemonic::random(&mut OsRng, Default::default());
|
||||
|
||||
println!("{}", mnemonic.phrase());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enc_dec_for_derived() -> Result<(), Box<dyn Error>> {
|
||||
let seed = {
|
||||
let mnemonic = Mnemonic::new(MNEMONIC_PHRASE, Language::English)?;
|
||||
mnemonic.to_seed("password")
|
||||
};
|
||||
let seed = global_seed()?;
|
||||
|
||||
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!(
|
||||
alice_sk.private_key().public_key().to_sec1_bytes(),
|
||||
hex::decode("02027e3510f66f1f6c1ea5e3600062255928e518220f7883810cac3fc7fc092057")
|
||||
alice_pk.to_sec1_bytes(),
|
||||
hex::decode("0219b0b8ee5fe9b317b69119fd15170d79737380c4f020e251b7839096f5513ccf")
|
||||
.unwrap()
|
||||
.into()
|
||||
);
|
||||
|
||||
let alice_sk_str = alice_sk.to_string(Prefix::XPRV).to_string();
|
||||
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 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 =
|
||||
ecies::decrypt(&alice_sk.private_key().to_bytes(), ciphertext.as_slice()).unwrap();
|
||||
|
|
|
@ -4,10 +4,11 @@ use uuid::Uuid;
|
|||
use crate::{
|
||||
obligato_client::Client,
|
||||
types::{ObligatoObligation, ObligatoSetOff},
|
||||
BANK_DEBTOR_ID,
|
||||
};
|
||||
|
||||
pub struct MockClient;
|
||||
pub struct MockClient {
|
||||
pub bank: Uuid,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Client for MockClient {
|
||||
|
@ -25,7 +26,7 @@ impl Client for MockClient {
|
|||
// tender: $ --10--> 1
|
||||
ObligatoObligation {
|
||||
id: Uuid::from_u128(2),
|
||||
debtor_id: Uuid::parse_str(BANK_DEBTOR_ID).unwrap(),
|
||||
debtor_id: self.bank,
|
||||
creditor_id: Uuid::from_u128(1),
|
||||
amount: 10,
|
||||
},
|
||||
|
|
|
@ -47,7 +47,13 @@ pub struct RawEncryptedObligation {
|
|||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
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)]
|
||||
|
|
Loading…
Reference in a new issue