From 735ac8414464dd3464e9e263e38cd90b6ffb4554 Mon Sep 17 00:00:00 2001 From: parazyd Date: Mon, 12 Jun 2023 09:55:51 +0200 Subject: [PATCH] contract/consensus: Create new output keypair for UnstakeRequest. --- .../src/client/unstake_request_v1.rs | 100 ++++++++++-------- .../consensus/src/entrypoint/stake_v1.rs | 6 +- .../consensus/tests/genesis_stake_unstake.rs | 10 +- src/contract/consensus/tests/harness.rs | 27 +++-- src/contract/consensus/tests/stake_unstake.rs | 12 ++- 5 files changed, 91 insertions(+), 64 deletions(-) diff --git a/src/contract/consensus/src/client/unstake_request_v1.rs b/src/contract/consensus/src/client/unstake_request_v1.rs index 8eea7e95a..aafb937ed 100644 --- a/src/contract/consensus/src/client/unstake_request_v1.rs +++ b/src/contract/consensus/src/client/unstake_request_v1.rs @@ -28,7 +28,7 @@ use darkfi_money_contract::{ model::{ConsensusInput, ConsensusOutput, ConsensusUnstakeReqParamsV1}, }; use darkfi_sdk::{ - crypto::{note::AeadEncryptedNote, pasta_prelude::*, MerkleTree, SecretKey}, + crypto::{note::AeadEncryptedNote, pasta_prelude::*, Keypair, MerkleTree, SecretKey}, incrementalmerkletree::Tree, pasta::pallas, }; @@ -41,15 +41,20 @@ use crate::client::common::{ }; pub struct ConsensusUnstakeRequestCallDebris { + /// Payload params pub params: ConsensusUnstakeReqParamsV1, + /// ZK proofs pub proofs: Vec, + /// The new output keypair (used in the minted coin) + pub keypair: Keypair, + /// Secret key used to sign the transaction pub signature_secret: SecretKey, } /// Struct holding necessary information to build a `Consensus::UnstakeRequestV1` contract call. pub struct ConsensusUnstakeRequestCallBuilder { /// `ConsensusOwnCoin` we're given to use in this builder - pub coin: ConsensusOwnCoin, + pub owncoin: ConsensusOwnCoin, /// Epoch unstaked coin is minted pub epoch: u64, /// Merkle tree of coins used to create inclusion proofs @@ -66,81 +71,82 @@ pub struct ConsensusUnstakeRequestCallBuilder { impl ConsensusUnstakeRequestCallBuilder { pub fn build(&self) -> Result { - debug!("Building Consensus::UnstakeRequestV1 contract call"); - assert!(self.coin.note.value != 0); + info!("Building Consensus::UnstakeRequestV1 contract call"); + assert!(self.owncoin.note.value != 0); - debug!("Building anonymous input"); - let leaf_position = self.coin.leaf_position; + debug!("Building Consensus::UnstakeRequestV1 anonymous input"); let root = self.tree.root(0).unwrap(); - let merkle_path = self.tree.authentication_path(leaf_position, &root).unwrap(); - let value_blind = pallas::Scalar::random(&mut OsRng); - let input = ConsensusBurnInputInfo { - leaf_position, - merkle_path, - secret: self.coin.secret, - note: self.coin.note.clone(), - value_blind, - }; - debug!("Finished building input"); + let merkle_path = self.tree.authentication_path(self.owncoin.leaf_position, &root).unwrap(); - info!("Creating unstake burn proof for input"); - let value_blind = input.value_blind; + let input = ConsensusBurnInputInfo { + leaf_position: self.owncoin.leaf_position, + merkle_path, + secret: self.owncoin.secret, + note: self.owncoin.note.clone(), + value_blind: pallas::Scalar::random(&mut OsRng), + }; + + debug!("Building Consensus::UnstakeRequestV1 anonymous output"); + let output_serial = pallas::Base::random(&mut OsRng); + let output_coin_blind = pallas::Base::random(&mut OsRng); + + // We create a new random keypair for the output + let output_keypair = Keypair::random(&mut OsRng); + + let output = ConsensusMintOutputInfo { + value: self.owncoin.note.value, + epoch: self.epoch, + public_key: output_keypair.public, + value_blind: input.value_blind, + serial: output_serial, + coin_blind: output_coin_blind, + }; + + info!("Building Consensus::UnstakeRequestV1 Burn ZK proof"); let (burn_proof, public_inputs, signature_secret) = create_consensus_burn_proof(&self.burn_zkbin, &self.burn_pk, &input)?; - let input = ConsensusInput { - epoch: self.coin.note.epoch, + let tx_input = ConsensusInput { + epoch: self.owncoin.note.epoch, value_commit: public_inputs.value_commit, nullifier: public_inputs.nullifier, merkle_root: public_inputs.merkle_root, signature_public: public_inputs.signature_public, }; - debug!("Building anonymous output"); - let serial = pallas::Base::random(&mut OsRng); - let coin_blind = pallas::Base::random(&mut OsRng); - let public_key = public_inputs.signature_public; - - let output = ConsensusMintOutputInfo { - value: self.coin.note.value, - epoch: self.epoch, - public_key, - value_blind, - serial, - coin_blind, - }; - debug!("Finished building output"); - - info!("Creating stake mint proof for output"); + info!("Building Consensus::UnstakeRequestV1 Mint ZK proof"); let (mint_proof, public_inputs) = create_consensus_mint_proof(&self.mint_zkbin, &self.mint_pk, &output)?; // Encrypted note let note = ConsensusNote { - serial, + serial: output_serial, value: output.value, - epoch: self.epoch, - coin_blind, - value_blind, + epoch: output.epoch, + coin_blind: output_coin_blind, + value_blind: input.value_blind, reward: 0, - reward_blind: value_blind, + reward_blind: pallas::Scalar::ZERO, }; let encrypted_note = AeadEncryptedNote::encrypt(¬e, &output.public_key, &mut OsRng)?; - let output = ConsensusOutput { + let tx_output = ConsensusOutput { value_commit: public_inputs.value_commit, coin: public_inputs.coin, note: encrypted_note, }; // We now fill this with necessary stuff - let params = ConsensusUnstakeReqParamsV1 { input, output }; - let proofs = vec![burn_proof, mint_proof]; + let params = ConsensusUnstakeReqParamsV1 { input: tx_input, output: tx_output }; - // Now we should have all the params, zk proof, and signature secret. - // We return it all and let the caller deal with it. - let debris = ConsensusUnstakeRequestCallDebris { params, proofs, signature_secret }; + // Construct debris + let debris = ConsensusUnstakeRequestCallDebris { + params, + proofs: vec![burn_proof, mint_proof], + keypair: output_keypair, + signature_secret, + }; Ok(debris) } } diff --git a/src/contract/consensus/src/entrypoint/stake_v1.rs b/src/contract/consensus/src/entrypoint/stake_v1.rs index 43180ebd8..023cc7d06 100644 --- a/src/contract/consensus/src/entrypoint/stake_v1.rs +++ b/src/contract/consensus/src/entrypoint/stake_v1.rs @@ -19,7 +19,7 @@ use darkfi_money_contract::{ error::MoneyError, model::{ConsensusStakeParamsV1, ConsensusStakeUpdateV1, MoneyStakeParamsV1}, - CONSENSUS_CONTRACT_INFO_TREE, CONSENSUS_CONTRACT_STAKED_COINS_TREE, + MoneyFunction, CONSENSUS_CONTRACT_INFO_TREE, CONSENSUS_CONTRACT_STAKED_COINS_TREE, CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE, CONSENSUS_CONTRACT_STAKED_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1, MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_NULLIFIERS_TREE, @@ -90,7 +90,7 @@ pub(crate) fn consensus_stake_process_instruction_v1( return Err(MoneyError::CallIdxOutOfBounds.into()) } - // Verify previous call corresponds to Money::StakeV1 (0x06) + // Verify previous call corresponds to Money::StakeV1 let previous_call_idx = call_idx - 1; let previous = &calls[previous_call_idx as usize]; if previous.contract_id.inner() != MONEY_CONTRACT_ID.inner() { @@ -98,7 +98,7 @@ pub(crate) fn consensus_stake_process_instruction_v1( return Err(MoneyError::StakePreviousCallNotMoneyContract.into()) } - if previous.data[0] != 0x06 { + if previous.data[0] != MoneyFunction::StakeV1 as u8 { msg!("[ConsensusStakeV1] Error: Previous call function mismatch"); return Err(MoneyError::PreviousCallFunctionMismatch.into()) } diff --git a/src/contract/consensus/tests/genesis_stake_unstake.rs b/src/contract/consensus/tests/genesis_stake_unstake.rs index b15583bd7..163f2a546 100644 --- a/src/contract/consensus/tests/genesis_stake_unstake.rs +++ b/src/contract/consensus/tests/genesis_stake_unstake.rs @@ -156,8 +156,12 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> { info!(target: "consensus", "[Alice] ==========================="); info!(target: "consensus", "[Alice] Building unstake request tx"); info!(target: "consensus", "[Alice] ==========================="); - let (unstake_request_tx, unstake_request_params, unstake_request_secret_key) = - th.unstake_request(Holder::Alice, current_slot, alice_rewarded_staked_oc.clone()).await?; + let ( + unstake_request_tx, + unstake_request_params, + unstake_request_output_secret_key, + _unstake_request_signature_secret_key, + ) = th.unstake_request(Holder::Alice, current_slot, alice_rewarded_staked_oc.clone()).await?; info!(target: "consensus", "[Faucet] =================================="); info!(target: "consensus", "[Faucet] Executing Alice unstake request tx"); @@ -187,7 +191,7 @@ async fn consensus_contract_genesis_stake_unstake() -> Result<()> { let alice_unstake_request_oc = th.gather_consensus_unstaked_owncoin( Holder::Alice, unstake_request_params.output, - Some(unstake_request_secret_key), + Some(unstake_request_output_secret_key), )?; // Verify values match diff --git a/src/contract/consensus/tests/harness.rs b/src/contract/consensus/tests/harness.rs index 7aa4cfc29..cbc2be34b 100644 --- a/src/contract/consensus/tests/harness.rs +++ b/src/contract/consensus/tests/harness.rs @@ -624,7 +624,7 @@ impl ConsensusTestHarness { holder: Holder, slot: u64, staked_oc: ConsensusOwnCoin, - ) -> Result<(Transaction, ConsensusUnstakeReqParamsV1, SecretKey)> { + ) -> Result<(Transaction, ConsensusUnstakeReqParamsV1, SecretKey, SecretKey)> { let wallet = self.holders.get_mut(&holder).unwrap(); let (burn_pk, burn_zkbin) = self.proving_keys.get(&CONSENSUS_CONTRACT_ZKAS_BURN_NS_V1).unwrap(); @@ -637,7 +637,7 @@ impl ConsensusTestHarness { // Building Consensus::Unstake params let unstake_request_call_debris = ConsensusUnstakeRequestCallBuilder { - coin: staked_oc.clone(), + owncoin: staked_oc.clone(), epoch, tree: wallet.consensus_staked_merkle_tree.clone(), burn_zkbin: burn_zkbin.clone(), @@ -646,9 +646,16 @@ impl ConsensusTestHarness { mint_pk: mint_pk.clone(), } .build()?; - let (unstake_request_params, unstake_request_proofs, unstake_request_secret_key) = ( + + let ( + unstake_request_params, + unstake_request_proofs, + unstake_request_output_keypair, + unstake_request_signature_secret_key, + ) = ( unstake_request_call_debris.params, unstake_request_call_debris.proofs, + unstake_request_call_debris.keypair, unstake_request_call_debris.signature_secret, ); @@ -659,19 +666,25 @@ impl ConsensusTestHarness { let calls = vec![call]; let proofs = vec![unstake_request_proofs]; let mut unstake_request_tx = Transaction { calls, proofs, signatures: vec![] }; - let sigs = unstake_request_tx.create_sigs(&mut OsRng, &[unstake_request_secret_key])?; + let sigs = + unstake_request_tx.create_sigs(&mut OsRng, &[unstake_request_signature_secret_key])?; unstake_request_tx.signatures = vec![sigs]; tx_action_benchmark.creation_times.push(timer.elapsed()); // Calculate transaction sizes let encoded: Vec = serialize(&unstake_request_tx); - let size = ::std::mem::size_of_val(&*encoded); + let size = std::mem::size_of_val(&*encoded); tx_action_benchmark.sizes.push(size); let base58 = bs58::encode(&encoded).into_string(); - let size = ::std::mem::size_of_val(&*base58); + let size = std::mem::size_of_val(&*base58); tx_action_benchmark.broadcasted_sizes.push(size); - Ok((unstake_request_tx, unstake_request_params, unstake_request_secret_key)) + Ok(( + unstake_request_tx, + unstake_request_params, + unstake_request_output_keypair.secret, + unstake_request_signature_secret_key, + )) } pub async fn execute_unstake_request_tx( diff --git a/src/contract/consensus/tests/stake_unstake.rs b/src/contract/consensus/tests/stake_unstake.rs index 1be220b3a..e47edffc4 100644 --- a/src/contract/consensus/tests/stake_unstake.rs +++ b/src/contract/consensus/tests/stake_unstake.rs @@ -156,8 +156,12 @@ async fn consensus_contract_stake_unstake() -> Result<()> { info!(target: "consensus", "[Alice] ==========================="); info!(target: "consensus", "[Alice] Building unstake request tx"); info!(target: "consensus", "[Alice] ==========================="); - let (unstake_request_tx, unstake_request_params, unstake_request_secret_key) = - th.unstake_request(Holder::Alice, current_slot, alice_rewarded_staked_oc.clone()).await?; + let ( + unstake_request_tx, + unstake_request_params, + unstake_request_output_secret_key, + unstake_request_signature_secret_key, + ) = th.unstake_request(Holder::Alice, current_slot, alice_rewarded_staked_oc.clone()).await?; info!(target: "consensus", "[Faucet] =================================="); info!(target: "consensus", "[Faucet] Executing Alice unstake request tx"); @@ -187,7 +191,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> { let alice_unstake_request_oc = th.gather_consensus_unstaked_owncoin( Holder::Alice, unstake_request_params.output, - Some(unstake_request_secret_key), + Some(unstake_request_output_secret_key), )?; // Verify values match @@ -207,7 +211,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> { info!(target: "consensus", "[Malicious] ============================="); info!(target: "consensus", "[Malicious] Checking unstaking coin again"); info!(target: "consensus", "[Malicious] ============================="); - let (unstake_request_tx, _, _) = + let (unstake_request_tx, _, _, _) = th.unstake_request(Holder::Alice, current_slot, alice_unstake_request_oc.clone()).await?; th.execute_erroneous_unstake_request_txs( Holder::Alice,