From 63cbf082719d75ddfd1ba21ee8ca1621e7dfb007 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Wed, 24 Jul 2024 16:29:03 +0200 Subject: [PATCH] fix proofs for revealed only --- Cargo.lock | 2 + .../src/handlers/accounts.rs | 17 ++++--- .../src/handlers/confidential.rs | 11 +++-- dan_layer/engine_types/Cargo.toml | 1 + .../engine_types/src/confidential/withdraw.rs | 10 +++- .../src/support/confidential.rs | 18 +++---- dan_layer/wallet/crypto/Cargo.toml | 1 + dan_layer/wallet/crypto/src/api.rs | 32 ++++++++++--- .../crypto/src/confidential_statement.rs | 1 - dan_layer/wallet/crypto/src/proof.rs | 48 +++++++++---------- .../wallet/crypto/tests/output_statement.rs | 12 +++++ .../crypto/tests/viewable_balance_proof.rs | 7 +-- .../sdk/src/apis/confidential_crypto.rs | 21 ++++++-- .../sdk/src/apis/confidential_transfer.rs | 31 ++++-------- 14 files changed, 131 insertions(+), 81 deletions(-) create mode 100644 dan_layer/wallet/crypto/tests/output_statement.rs diff --git a/Cargo.lock b/Cargo.lock index c05ebcfde..7576421ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9223,6 +9223,7 @@ dependencies = [ "blake2", "chacha20poly1305", "digest", + "log", "rand", "tari_crypto", "tari_engine_types", @@ -9346,6 +9347,7 @@ dependencies = [ "digest", "hex", "lazy_static", + "log", "rand", "serde", "serde_json", diff --git a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs index 973603156..95ef8ada1 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/accounts.rs @@ -363,7 +363,6 @@ pub async fn handle_reveal_funds( sender_public_nonce: public_nonce, minimum_value_promise: 0, encrypted_data, - reveal_amount: amount_to_reveal, resource_view_key: None, }; @@ -371,9 +370,14 @@ pub async fn handle_reveal_funds( .confidential_outputs_api() .resolve_output_masks(inputs, key_manager::TRANSACTION_BRANCH)?; - let reveal_proof = - sdk.confidential_crypto_api() - .generate_withdraw_proof(&inputs, Amount::zero(), &output_statement, None)?; + let reveal_proof = sdk.confidential_crypto_api().generate_withdraw_proof( + &inputs, + Amount::zero(), + Some(&output_statement), + amount_to_reveal, + None, + Amount::zero(), + )?; info!( target: LOG_TARGET, @@ -590,15 +594,16 @@ pub async fn handle_claim_burn( sender_public_nonce: output_public_nonce, minimum_value_promise: 0, encrypted_data, - reveal_amount: max_fee, resource_view_key: None, }; let reveal_proof = sdk.confidential_crypto_api().generate_withdraw_proof( &[unmasked_output], Amount::zero(), - &output_statement, + Some(&output_statement).filter(|o| !o.amount.is_zero()), + max_fee, None, + Amount::zero(), )?; let instructions = vec![Instruction::ClaimBurn { diff --git a/applications/tari_dan_wallet_daemon/src/handlers/confidential.rs b/applications/tari_dan_wallet_daemon/src/handlers/confidential.rs index f7b782e00..6dc7691ae 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/confidential.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/confidential.rs @@ -120,7 +120,6 @@ pub async fn handle_create_transfer_proof( sender_public_nonce: public_nonce, minimum_value_promise: 0, encrypted_data, - reveal_amount: req.reveal_amount, resource_view_key: resource_view_key.clone(), }; @@ -155,7 +154,6 @@ pub async fn handle_create_transfer_proof( sender_public_nonce: public_nonce, encrypted_data, minimum_value_promise: 0, - reveal_amount: Amount::zero(), resource_view_key, }) } else { @@ -170,8 +168,10 @@ pub async fn handle_create_transfer_proof( &inputs, // TODO: support for using revealed funds as input for proof generation Amount::zero(), - &output_statement, + Some(&output_statement).filter(|o| !o.amount.is_zero()), + req.reveal_amount, maybe_change_statement.as_ref(), + Amount::zero(), )?; Ok(ProofsGenerateResponse { proof_id, proof }) @@ -230,11 +230,12 @@ pub async fn handle_create_output_proof( sender_public_nonce: public_nonce, minimum_value_promise: 0, encrypted_data, - reveal_amount: Amount::zero(), // TODO: the request must include the resource address so that we can fetch the view key resource_view_key: None, }; - let proof = sdk.confidential_crypto_api().generate_output_proof(&statement)?; + let proof = sdk + .confidential_crypto_api() + .generate_output_proof(&statement, Amount::zero())?; Ok(ConfidentialCreateOutputProofResponse { proof }) } diff --git a/dan_layer/engine_types/Cargo.toml b/dan_layer/engine_types/Cargo.toml index 4fb239773..fdcd8c200 100644 --- a/dan_layer/engine_types/Cargo.toml +++ b/dan_layer/engine_types/Cargo.toml @@ -28,6 +28,7 @@ serde = { workspace = true, default-features = true } serde_json = { workspace = true } thiserror = { workspace = true } ts-rs = { workspace = true, optional = true } +log = { workspace = true } [features] default = [] diff --git a/dan_layer/engine_types/src/confidential/withdraw.rs b/dan_layer/engine_types/src/confidential/withdraw.rs index 0aa81b198..0cf8bc5c7 100644 --- a/dan_layer/engine_types/src/confidential/withdraw.rs +++ b/dan_layer/engine_types/src/confidential/withdraw.rs @@ -114,14 +114,20 @@ pub(crate) fn validate_confidential_withdraw<'a, I: IntoIterator>(), input_revealed_amount, - &output_proof, + Some(&output_proof), + revealed_output_amount, change_proof.as_ref(), + Amount::zero(), ) .unwrap(); diff --git a/dan_layer/wallet/crypto/Cargo.toml b/dan_layer/wallet/crypto/Cargo.toml index e1f50a481..ca6455913 100644 --- a/dan_layer/wallet/crypto/Cargo.toml +++ b/dan_layer/wallet/crypto/Cargo.toml @@ -19,6 +19,7 @@ digest = { workspace = true } rand = { workspace = true } thiserror = { workspace = true } zeroize = { workspace = true } +log = { workspace = true } [dev-dependencies] tari_template_test_tooling = { workspace = true } diff --git a/dan_layer/wallet/crypto/src/api.rs b/dan_layer/wallet/crypto/src/api.rs index d9e9fce70..abd45e033 100644 --- a/dan_layer/wallet/crypto/src/api.rs +++ b/dan_layer/wallet/crypto/src/api.rs @@ -25,10 +25,17 @@ use crate::{ pub fn create_withdraw_proof( inputs: &[ConfidentialOutputMaskAndValue], input_revealed_amount: Amount, - output_statement: &ConfidentialProofStatement, + output_statement: Option<&ConfidentialProofStatement>, + output_revealed_amount: Amount, change_statement: Option<&ConfidentialProofStatement>, + change_revealed_amount: Amount, ) -> Result { - let output_proof = create_confidential_output_statement(output_statement, change_statement)?; + let output_proof = create_confidential_output_statement( + output_statement, + output_revealed_amount, + change_statement, + change_revealed_amount, + )?; let (input_commitments, agg_input_mask) = inputs.iter().fold( (Vec::with_capacity(inputs.len()), RistrettoSecretKey::default()), |(mut commitments, agg_input), input| { @@ -44,7 +51,7 @@ pub fn create_withdraw_proof( let balance_proof = generate_balance_proof( &agg_input_mask, input_revealed_amount, - &output_statement.mask, + output_statement.as_ref().map(|o| &o.mask), change_statement.as_ref().map(|ch| &ch.mask), output_revealed_amount, ); @@ -139,17 +146,28 @@ fn create_commitment(mask: &RistrettoSecretKey, value: u64) -> PedersenCommitmen fn generate_balance_proof( input_mask: &RistrettoSecretKey, input_revealed_amount: Amount, - output_mask: &RistrettoSecretKey, + output_mask: Option<&RistrettoSecretKey>, change_mask: Option<&RistrettoSecretKey>, output_reveal_amount: Amount, ) -> BalanceProofSignature { - let secret_excess = input_mask - output_mask - change_mask.unwrap_or(&RistrettoSecretKey::default()); + let secret_excess = input_mask - + output_mask.unwrap_or(&RistrettoSecretKey::default()) - + change_mask.unwrap_or(&RistrettoSecretKey::default()); + if secret_excess == RistrettoSecretKey::default() { + // This is a revealed only proof + return BalanceProofSignature::zero(); + } let excess = RistrettoPublicKey::from_secret_key(&secret_excess); let (nonce, public_nonce) = RistrettoPublicKey::random_keypair(&mut OsRng); - let challenge = + const LOG_TARGET: &str = "tari::dan::wallet::confidential::withdraw"; + log::error!(target: LOG_TARGET, "🐞W public_excess: {excess}"); + log::error!(target: LOG_TARGET, "🐞W public_nonce: {}", public_nonce); + log::error!(target: LOG_TARGET, "🐞W input_revealed_amount: {input_revealed_amount}"); + log::error!(target: LOG_TARGET, "🐞W output_revealed_amount: {output_reveal_amount}"); + let message = challenges::confidential_withdraw64(&excess, &public_nonce, input_revealed_amount, output_reveal_amount); - let sig = RistrettoSchnorr::sign_raw_uniform(&secret_excess, nonce, &challenge).unwrap(); + let sig = RistrettoSchnorr::sign_raw_uniform(&secret_excess, nonce, &message).unwrap(); BalanceProofSignature::try_from_parts(sig.get_public_nonce().as_bytes(), sig.get_signature().as_bytes()).unwrap() } diff --git a/dan_layer/wallet/crypto/src/confidential_statement.rs b/dan_layer/wallet/crypto/src/confidential_statement.rs index 3c7605faf..3a35ccc80 100644 --- a/dan_layer/wallet/crypto/src/confidential_statement.rs +++ b/dan_layer/wallet/crypto/src/confidential_statement.rs @@ -15,7 +15,6 @@ pub struct ConfidentialProofStatement { pub sender_public_nonce: RistrettoPublicKey, pub minimum_value_promise: u64, pub encrypted_data: EncryptedData, - pub reveal_amount: Amount, pub resource_view_key: Option, } diff --git a/dan_layer/wallet/crypto/src/proof.rs b/dan_layer/wallet/crypto/src/proof.rs index 6322f600f..51bec9078 100644 --- a/dan_layer/wallet/crypto/src/proof.rs +++ b/dan_layer/wallet/crypto/src/proof.rs @@ -36,6 +36,7 @@ use tari_hashing::TransactionSecureNonceKdfDomain; use tari_template_lib::{ crypto::RistrettoPublicKeyBytes, models::{ + Amount, ConfidentialOutputStatement, ConfidentialStatement, EncryptedData, @@ -54,8 +55,10 @@ use crate::{ }; pub fn create_confidential_output_statement( - output_statement: &ConfidentialProofStatement, + output_statement: Option<&ConfidentialProofStatement>, + output_revealed_amount: Amount, change_statement: Option<&ConfidentialProofStatement>, + change_revealed_amount: Amount, ) -> Result { let proof_change_statement = change_statement .as_ref() @@ -78,38 +81,34 @@ pub fn create_confidential_output_statement( }) }) .transpose()?; - let confidential_output_value = output_statement - .amount + .as_ref() + .map(|o| o.amount) + .unwrap_or_default() .as_u64_checked() .ok_or(ConfidentialProofError::NegativeAmount)?; - let proof_output_statement = if confidential_output_value == 0 { - None - } else { - let commitment = output_statement.to_commitment(); - let statement = Some(ConfidentialStatement { + let proof_output_statement = output_statement.as_ref().map(|stmt| { + let commitment = stmt.to_commitment(); + ConfidentialStatement { commitment: copy_fixed(commitment.as_bytes()), - sender_public_nonce: copy_fixed(output_statement.sender_public_nonce.as_bytes()), - encrypted_data: output_statement.encrypted_data.clone(), - minimum_value_promise: output_statement.minimum_value_promise, - viewable_balance_proof: output_statement.resource_view_key.as_ref().map(|view_key| { - create_viewable_balance_proof(&output_statement.mask, confidential_output_value, &commitment, view_key) + sender_public_nonce: copy_fixed(stmt.sender_public_nonce.as_bytes()), + encrypted_data: stmt.encrypted_data.clone(), + minimum_value_promise: stmt.minimum_value_promise, + viewable_balance_proof: stmt.resource_view_key.as_ref().map(|view_key| { + create_viewable_balance_proof(&stmt.mask, confidential_output_value, &commitment, view_key) }), - }); - - statement - }; + } + }); - let output_range_proof = - generate_extended_bullet_proof(Some(output_statement).filter(|s| !s.amount.is_zero()), change_statement)?; + let output_range_proof = generate_extended_bullet_proof(output_statement, change_statement)?; Ok(ConfidentialOutputStatement { output_statement: proof_output_statement, change_statement: proof_change_statement, range_proof: output_range_proof, - output_revealed_amount: output_statement.reveal_amount, - change_revealed_amount: change_statement.map(|stmt| stmt.reveal_amount).unwrap_or_default(), + output_revealed_amount, + change_revealed_amount, }) } @@ -314,16 +313,17 @@ mod tests { fn create_valid_proof(amount: Amount, minimum_value_promise: u64) -> ConfidentialOutputStatement { let mask = RistrettoSecretKey::random(&mut OsRng); create_confidential_output_statement( - &ConfidentialProofStatement { + Some(&ConfidentialProofStatement { amount, minimum_value_promise, mask, sender_public_nonce: Default::default(), - reveal_amount: Default::default(), encrypted_data: EncryptedData([0u8; EncryptedData::size()]), resource_view_key: None, - }, + }), + Default::default(), None, + Default::default(), ) .unwrap() } diff --git a/dan_layer/wallet/crypto/tests/output_statement.rs b/dan_layer/wallet/crypto/tests/output_statement.rs new file mode 100644 index 000000000..73358de99 --- /dev/null +++ b/dan_layer/wallet/crypto/tests/output_statement.rs @@ -0,0 +1,12 @@ +// Copyright 2024 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use tari_dan_wallet_crypto::create_withdraw_proof; +use tari_template_lib::models::Amount; + +#[test] +fn it_create_a_valid_revealed_only_proof() { + let proof = create_withdraw_proof(&[], Amount(123), None, Amount(123), None, Amount(0)).unwrap(); + + assert!(proof.is_revealed_only()); +} diff --git a/dan_layer/wallet/crypto/tests/viewable_balance_proof.rs b/dan_layer/wallet/crypto/tests/viewable_balance_proof.rs index 517d93dee..a812433d8 100644 --- a/dan_layer/wallet/crypto/tests/viewable_balance_proof.rs +++ b/dan_layer/wallet/crypto/tests/viewable_balance_proof.rs @@ -21,7 +21,6 @@ fn create_output_statement(value: Amount, view_key: &RistrettoPublicKey) -> Conf sender_public_nonce: Default::default(), minimum_value_promise: 0, encrypted_data: Default::default(), - reveal_amount: Default::default(), resource_view_key: Some(view_key.clone()), } } @@ -44,7 +43,8 @@ fn it_errors_no_balance_proof_with_view_key() { let (_, view_key) = keypair_from_seed(1); let output_statement = create_output_statement(123.into(), &view_key); - let proof = create_confidential_output_statement(&output_statement, None).unwrap(); + let proof = + create_confidential_output_statement(Some(&output_statement), Amount::zero(), None, Amount::zero()).unwrap(); let output_statement = proof.output_statement.as_ref().unwrap(); let viewable_balance_proof = proof .output_statement @@ -69,7 +69,8 @@ fn it_generates_a_valid_proof() { let output_statement = create_output_statement(123.into(), &view_key); let timer = Instant::now(); - let proof = create_confidential_output_statement(&output_statement, None).unwrap(); + let proof = + create_confidential_output_statement(Some(&output_statement), Amount::zero(), None, Amount::zero()).unwrap(); let gen_proof_time = timer.elapsed(); let output_statement = proof.output_statement.as_ref().unwrap(); diff --git a/dan_layer/wallet/sdk/src/apis/confidential_crypto.rs b/dan_layer/wallet/sdk/src/apis/confidential_crypto.rs index 6e02a9480..4540881c3 100644 --- a/dan_layer/wallet/sdk/src/apis/confidential_crypto.rs +++ b/dan_layer/wallet/sdk/src/apis/confidential_crypto.rs @@ -39,10 +39,19 @@ impl ConfidentialCryptoApi { &self, inputs: &[ConfidentialOutputMaskAndValue], input_revealed_amount: Amount, - output_statement: &ConfidentialProofStatement, + output_statement: Option<&ConfidentialProofStatement>, + output_revealed_amount: Amount, change_statement: Option<&ConfidentialProofStatement>, + change_revealed_amount: Amount, ) -> Result { - let proof = create_withdraw_proof(inputs, input_revealed_amount, output_statement, change_statement)?; + let proof = create_withdraw_proof( + inputs, + input_revealed_amount, + output_statement, + output_revealed_amount, + change_statement, + change_revealed_amount, + )?; Ok(proof) } @@ -70,8 +79,14 @@ impl ConfidentialCryptoApi { pub fn generate_output_proof( &self, statement: &ConfidentialProofStatement, + revealed_amount: Amount, ) -> Result { - let proof = create_confidential_output_statement(statement, None)?; + let proof = create_confidential_output_statement( + Some(statement).filter(|s| !s.amount.is_zero()), + revealed_amount, + None, + Amount::zero(), + )?; Ok(proof) } diff --git a/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs b/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs index 772f59919..a56e51b8e 100644 --- a/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs +++ b/dan_layer/wallet/sdk/src/apis/confidential_transfer.rs @@ -15,7 +15,7 @@ use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; use tari_template_lib::{ args, constants::CONFIDENTIAL_TARI_RESOURCE_ADDRESS, - models::{Amount, ComponentAddress, EncryptedData, ResourceAddress}, + models::{Amount, ComponentAddress, ResourceAddress}, }; use tari_transaction::Transaction; @@ -304,13 +304,7 @@ where // No change necessary None } else { - let statement = self.create_confidential_proof_statement( - &account_public_key, - confidential_change, - // We always withdraw the exact amount of revealed required - Amount::zero(), - None, - )?; + let statement = self.create_confidential_proof_statement(&account_public_key, confidential_change, None)?; self.outputs_api.add_output(ConfidentialOutputModel { account_address: account.address.clone(), @@ -334,16 +328,11 @@ where let fee_withdraw_proof = self.crypto_api.generate_withdraw_proof( fee_inputs_to_spend.confidential.as_slice(), fee_inputs_to_spend.revealed, - &ConfidentialProofStatement { - amount: Amount::zero(), - mask: PrivateKey::default(), - sender_public_nonce: PublicKey::default(), - encrypted_data: EncryptedData::default(), - minimum_value_promise: 0, - reveal_amount: params.max_fee, - resource_view_key: None, - }, + None, + params.max_fee, maybe_fee_change_statement.as_ref(), + // We always withdraw the exact amount of revealed required + Amount::zero(), )?; // Reserve and lock input funds @@ -395,7 +384,6 @@ where let output_statement = self.create_confidential_proof_statement( ¶ms.destination_public_key, params.confidential_amount(), - params.revealed_amount(), resource_view_key.clone(), )?; @@ -416,7 +404,6 @@ where let statement = self.create_confidential_proof_statement( &account_public_key, change_confidential_amount, - Amount::zero(), resource_view_key, )?; @@ -443,8 +430,10 @@ where let proof = self.crypto_api.generate_withdraw_proof( &inputs_to_spend.confidential, inputs_to_spend.revealed, - &output_statement, + Some(&output_statement).filter(|o| !o.amount.is_zero()), + params.revealed_amount(), maybe_change_statement.as_ref(), + Amount::zero(), )?; let mut builder = Transaction::builder() @@ -495,7 +484,6 @@ where &self, dest_public_key: &PublicKey, confidential_amount: Amount, - reveal_amount: Amount, resource_view_key: Option, ) -> Result { let mask = if confidential_amount.is_zero() { @@ -520,7 +508,6 @@ where sender_public_nonce: public_nonce, encrypted_data, minimum_value_promise: 0, - reveal_amount, resource_view_key, }) }