From 5327f2cdda484bc5525563db64adb9023e714ee4 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 3 Sep 2024 16:06:35 +0100 Subject: [PATCH] [CONTRACTS] Add Owner API function to create mwixnet request (#720) * update srs test * updating comsig creation, attempt to get working * implementation of comsig given an output commit * lock output on mwixnet creation, add further testing --- api/src/owner.rs | 7 +- controller/tests/contract_srs_mwmixnet.rs | 49 ++++++++- libwallet/src/api_impl/owner.rs | 117 ++++++++++++++-------- libwallet/src/contract/utils.rs | 1 + libwallet/src/internal/selection.rs | 6 +- libwallet/src/mwixnet/types.rs | 2 +- 6 files changed, 128 insertions(+), 54 deletions(-) diff --git a/api/src/owner.rs b/api/src/owner.rs index d8e69e72..fa7157dd 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -39,6 +39,7 @@ use crate::libwallet::{ }; use crate::util::logger::LoggingConfig; use crate::util::secp::key::SecretKey; +use crate::util::secp::pedersen; use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString}; use grin_wallet_util::OnionV3Address; use std::convert::TryFrom; @@ -835,12 +836,12 @@ where &self, keychain_mask: Option<&SecretKey>, params: &MixnetReqCreationParams, - slate: &Slate, - // use_test_rng: bool, + commitment: &pedersen::Commitment, + lock_output: bool, // use_test_rng: bool, ) -> Result { let mut w_lock = self.wallet_inst.lock(); let w = w_lock.lc_provider()?.wallet_inst()?; - owner::create_mwixnet_req(&mut **w, keychain_mask, params, slate) + owner::create_mwixnet_req(&mut **w, keychain_mask, params, commitment, lock_output) } /// Processes an invoice tranaction created by another party, essentially diff --git a/controller/tests/contract_srs_mwmixnet.rs b/controller/tests/contract_srs_mwmixnet.rs index fb66a759..59a336dd 100644 --- a/controller/tests/contract_srs_mwmixnet.rs +++ b/controller/tests/contract_srs_mwmixnet.rs @@ -17,12 +17,12 @@ extern crate grin_wallet_controller as wallet; extern crate grin_wallet_impls as impls; extern crate log; +use grin_util::{secp::SecretKey, static_secp_instance}; use grin_wallet_libwallet as libwallet; use impls::test_framework::{self}; use libwallet::contract::my_fee_contribution; use libwallet::contract::types::{ContractNewArgsAPI, ContractSetupArgsAPI}; -use libwallet::mwixnet::onion::crypto::secp; use libwallet::mwixnet::types::MixnetReqCreationParams; use libwallet::{Slate, SlateState, TxLogEntryType}; use std::sync::atomic::Ordering; @@ -83,13 +83,52 @@ fn contract_srs_mwixnet_tx_impl(test_dir: &'static str) -> Result<(), libwallet: assert_eq!(slate.state, SlateState::Standard3); wallet::controller::owner_single_use(Some(send_wallet.clone()), send_mask, None, |api, m| { - let server_key_1 = secp::random_secret(); - let server_key_2 = secp::random_secret(); + api.post_tx(m, &slate, false)?; + Ok(()) + })?; + bh += 1; + + let _ = + test_framework::award_blocks_to_wallet(&chain, send_wallet.clone(), send_mask, 3, false); + bh += 3; + + // Recipient wallet sends outputs to mwixnet + wallet::controller::owner_single_use(Some(recv_wallet.clone()), recv_mask, None, |api, m| { + let secp_locked = static_secp_instance(); + let secp = secp_locked.lock(); + let server_pubkey_str_1 = + "97444ae673bb92c713c1a2f7b8882ffbfc1c67401a280a775dce1a8651584332"; + let server_pubkey_str_2 = + "0c9414341f2140ed34a5a12a6479bf5a6404820d001ab81d9d3e8cc38f049b4e"; + let server_pubkey_str_3 = + "b58ece97d60e71bb7e53218400b0d67bfe6a3cb7d3b4a67a44f8fb7c525cbca5"; + let server_key_1 = + SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_1).unwrap()) + .unwrap(); + let server_key_2 = + SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_2).unwrap()) + .unwrap(); + let server_key_3 = + SecretKey::from_slice(&secp, &grin_util::from_hex(&server_pubkey_str_3).unwrap()) + .unwrap(); let params = MixnetReqCreationParams { - server_keys: vec![server_key_1, server_key_2], + server_keys: vec![server_key_1, server_key_2, server_key_3], fee_per_hop: 50_000_000, }; - //api.create_mwixnet_req(send_mask, ¶ms, &slate)?; + let outputs = api.retrieve_outputs(recv_mask, false, false, None)?; + // get last output + let last_output = outputs.1[outputs.1.len() - 1].clone(); + + let mwixnet_req = api.create_mwixnet_req(m, ¶ms, &last_output.commit, true)?; + + println!("MWIXNET REQ: {:?}", mwixnet_req); + + // check output we created comsig for is indeed locked + let outputs = api.retrieve_outputs(recv_mask, false, false, None)?; + // get last output + let last_output = outputs.1[outputs.1.len() - 1].clone(); + assert!(last_output.output.status == libwallet::OutputStatus::Locked); + Ok(()) })?; diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs index 5f9f743e..962c21d2 100644 --- a/libwallet/src/api_impl/owner.rs +++ b/libwallet/src/api_impl/owner.rs @@ -19,7 +19,7 @@ use uuid::Uuid; use crate::api_impl::foreign::finalize_tx as foreign_finalize; use crate::contract::proofs::{InvoiceProof, ProofWitness}; use crate::grin_core::core::hash::Hashed; -use crate::grin_core::core::{Output, OutputFeatures, Transaction}; +use crate::grin_core::core::{FeeFields, Output, OutputFeatures, Transaction}; use crate::grin_core::libtx::proof; use crate::grin_keychain::ViewKey; use crate::grin_util::secp::key::SecretKey; @@ -49,6 +49,7 @@ use chrono::prelude::{DateTime, NaiveDateTime, Utc}; use ed25519_dalek::PublicKey as DalekPublicKey; use ed25519_dalek::SecretKey as DalekSecretKey; use ed25519_dalek::Verifier; +use x25519_dalek::{PublicKey as xPublicKey, StaticSecret}; use std::convert::{TryFrom, TryInto}; use std::sync::mpsc::Sender; @@ -1608,56 +1609,88 @@ pub fn create_mwixnet_req<'a, T: ?Sized, C, K>( w: &mut T, keychain_mask: Option<&SecretKey>, params: &MixnetReqCreationParams, - slate: &Slate, - // use_test_rng: bool, + commitment: &pedersen::Commitment, + lock_output: bool, ) -> Result where T: WalletBackend<'a, C, K>, C: NodeClient + 'a, K: Keychain + 'a, { - let context = w.get_private_context(keychain_mask, slate.id.as_bytes())?; + let parent_key_id = w.parent_key_id(); + let keychain = w.keychain(keychain_mask)?; + let outputs = updater::retrieve_outputs(w, keychain_mask, false, None, Some(&parent_key_id))?; - let my_keys = context.get_private_keys(); - let kernel = slate.tx_or_err()?.kernels()[0]; - - let msg = kernel.msg_to_sign()?; - - let comsig = ComSignature::sign(slate.amount, &my_keys.0, &msg.to_hex().as_bytes().to_vec())?; - - let mut hops: Vec = Vec::new(); - let mut final_commit = kernel.excess.clone(); - let mut final_blind = my_keys.0.clone(); - - for i in 0..params.server_keys.len() { - let excess = params.server_keys[i].clone(); - - let secp = secp256k1zkp::Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit); - final_blind.add_assign(&secp, &excess).unwrap(); - final_commit = add_excess(&final_commit, &excess).unwrap(); - let proof = if i == params.server_keys.len() - 1 { - let n1 = random_secret(); - let rp = secp.bullet_proof( - slate.amount - (params.fee_per_hop * params.server_keys.len() as u32) as u64, - final_blind.clone(), - n1.clone(), - n1.clone(), - None, - None, - ); - assert!(secp.verify_bullet_proof(final_commit, rp, None).is_ok()); - Some(rp) - } else { - None - }; - - let hop = new_hop(¶ms.server_keys[i], &excess, params.fee_per_hop, proof); - hops.push(hop); + let mut output = None; + for o in &outputs { + if o.commit == *commitment { + output = Some(o.output.clone()); + break; + } } - let onion = create_onion(&kernel.excess, &hops)?; + if output.is_none() { + return Err(Error::GenericError(String::from("output not found"))); + } + + let amount = output.clone().unwrap().value; + let input_blind = keychain.derive_key( + amount, + &output.clone().unwrap().key_id, + SwitchCommitmentType::Regular, + )?; + + let mut server_pubkeys = vec![]; + for i in 0..params.server_keys.len() { + server_pubkeys.push(xPublicKey::from(&StaticSecret::from( + params.server_keys[i].0, + ))); + } + + let fee = grin_core::libtx::tx_fee(1, 1, 1); + let new_amount = amount - (fee * server_pubkeys.len() as u64); + let new_output = build_output(w, keychain_mask, OutputFeatures::Plain, new_amount)?; + let secp = keychain.secp(); + + let mut blind_sum = new_output + .blind + .split(&BlindingFactor::from_secret_key(input_blind.clone()), &secp)?; + + let hops = server_pubkeys + .iter() + .enumerate() + .map(|(i, &p)| { + if (i + 1) == server_pubkeys.len() { + Hop { + server_pubkey: p.clone(), + excess: blind_sum.secret_key(&secp).unwrap(), + fee: FeeFields::from(fee as u32), + rangeproof: Some(new_output.output.proof.clone()), + } + } else { + let hop_excess = BlindingFactor::rand(&secp); + blind_sum = blind_sum.split(&hop_excess, &secp).unwrap(); + Hop { + server_pubkey: p.clone(), + excess: hop_excess.secret_key(&secp).unwrap(), + fee: FeeFields::from(fee as u32), + rangeproof: None, + } + } + }) + .collect(); + + let onion = create_onion(&commitment, &hops).unwrap(); + let comsig = ComSignature::sign(amount, &input_blind, &onion.serialize().unwrap()).unwrap(); + + // Lock output if requested + if lock_output { + let mut batch = w.batch(keychain_mask)?; + let mut update_output = batch.get(&output.as_ref().unwrap().key_id, &None)?; + update_output.lock(); + batch.lock_output(&mut update_output)?; + batch.commit()?; + } Ok(SwapReq { comsig, onion }) - - //slate.find_index_matching_context(&keychain, &context) } diff --git a/libwallet/src/contract/utils.rs b/libwallet/src/contract/utils.rs index 6e2dcbc7..e5007dcf 100644 --- a/libwallet/src/contract/utils.rs +++ b/libwallet/src/contract/utils.rs @@ -24,6 +24,7 @@ use crate::types::{Context, NodeClient, StoredProofInfo, TxLogEntryType, WalletB use crate::util::OnionV3Address; use crate::{address, Error, OutputData, OutputStatus, TxLogEntry}; use grin_core::core::FeeFields; +use grin_util::file::delete; use uuid::Uuid; /// Creates an initial TxLogEntry without input/output or kernel information diff --git a/libwallet/src/internal/selection.rs b/libwallet/src/internal/selection.rs index 4170b07a..9bfbbcb6 100644 --- a/libwallet/src/internal/selection.rs +++ b/libwallet/src/internal/selection.rs @@ -210,9 +210,9 @@ where sender_address: sender_address.to_ed25519()?, sender_address_path, sender_signature: None, - /// TODO: Will fill these as separate steps for now, check whether this - /// can be merged in a general case (which means knowing which nonces here belong to - /// the recipient) + // TODO: Will fill these as separate steps for now, check whether this + // can be merged in a general case (which means knowing which nonces here belong to + // the recipient) proof_type: None, receiver_public_nonce: None, receiver_public_excess: None, diff --git a/libwallet/src/mwixnet/types.rs b/libwallet/src/mwixnet/types.rs index 2339fa30..1f36ddea 100644 --- a/libwallet/src/mwixnet/types.rs +++ b/libwallet/src/mwixnet/types.rs @@ -23,7 +23,7 @@ use crate::grin_util::secp::key::SecretKey; use serde::{Deserialize, Serialize}; /// A Swap request -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct SwapReq { /// Com signature #[serde(with = "comsig::comsig_serde")]