Răsfoiți Sursa

contract/consensus: StakeV1 fixed

aggstam 3 ani în urmă
părinte
comite
284a19d85c

+ 3 - 0
src/contract/consensus/src/client/stake_v1.rs

@@ -88,6 +88,8 @@ pub struct ConsensusStakeCallBuilder {
     pub nullifier: Nullifier,
     /// Revealed Merkle root
     pub merkle_root: MerkleNode,
+    /// Public key for the signature
+    pub signature_public: PublicKey,
     /// `Consensus_Mint_V1` zkas circuit ZkBinary
     pub mint_zkbin: ZkBinary,
     /// Proving key for the `Consensus_Mint_V1` zk circuit
@@ -153,6 +155,7 @@ impl ConsensusStakeCallBuilder {
             value_commit: public_inputs.value_commit,
             nullifier: self.nullifier,
             merkle_root: self.merkle_root,
+            signature_public: self.signature_public,
         };
 
         // We now fill this with necessary stuff

+ 21 - 10
src/contract/consensus/src/entrypoint/stake_v1.rs

@@ -24,8 +24,8 @@ use darkfi_money_contract::{
 };
 use darkfi_sdk::{
     crypto::{
-        pasta_prelude::*, pedersen_commitment_base, Coin, ContractId, MerkleNode, DARK_TOKEN_ID,
-        MONEY_CONTRACT_ID,
+        pasta_prelude::*, pedersen_commitment_base, Coin, ContractId, MerkleNode, PublicKey,
+        DARK_TOKEN_ID, MONEY_CONTRACT_ID,
     },
     db::{db_contains_key, db_lookup, db_set},
     error::{ContractError, ContractResult},
@@ -51,6 +51,8 @@ pub(crate) fn consensus_stake_get_metadata_v1(
 
     // Public inputs for the ZK proofs we have to verify
     let mut zk_public_inputs: Vec<(String, Vec<pallas::Base>)> = vec![];
+    // Public keys for the transaction signatures we have to verify
+    let mut signature_pubkeys: Vec<PublicKey> = vec![];
 
     // Grab the pedersen commitment from the anonymous output
     let output = &params.output;
@@ -68,9 +70,12 @@ pub(crate) fn consensus_stake_get_metadata_v1(
         ],
     ));
 
+    signature_pubkeys.push(params.input.signature_public);
+
     // Serialize everything gathered and return it
     let mut metadata = vec![];
     zk_public_inputs.encode(&mut metadata)?;
+    signature_pubkeys.encode(&mut metadata)?;
 
     Ok(metadata)
 }
@@ -118,16 +123,22 @@ pub(crate) fn consensus_stake_process_instruction_v1(
     }
 
     // The nullifiers should already exist. It is the double-mint protection.
-    if !db_contains_key(money_nullifiers_db, &serialize(&input.nullifier))? {
-        msg!("[ConsensusStakeV1] Error: Duplicate nullifier found");
-        return Err(MoneyError::DuplicateNullifier.into())
+    if db_contains_key(money_nullifiers_db, &serialize(&input.nullifier))? {
+        msg!("[ConsensusStakeV1] Error: Missing nullifier");
+        return Err(MoneyError::StakeMissingNullifier.into())
+    }
+
+    // Check previous call is money contract
+    if call_idx == 0 {
+        msg!("[MoneyStakeV1] Error: previous_call_idx will be out of bounds");
+        return Err(MoneyError::SpendHookOutOfBounds.into())
     }
 
-    // Check caller matches stake spend hook and its correctness
-    let caller = &calls[call_idx as usize];
-    if caller.contract_id.inner() != MONEY_CONTRACT_ID.inner() {
-        msg!("[ConsensusStakeV1] Error: Invoking contract call does not match spend hook");
-        return Err(MoneyError::SpendHookMismatch.into())
+    let previous_call_idx = call_idx - 1;
+    let previous = &calls[previous_call_idx as usize];
+    if previous.contract_id.inner() != MONEY_CONTRACT_ID.inner() {
+        msg!("[MoneyStakeV1] Error: Previous contract call is not consensus contract");
+        return Err(MoneyError::StakePreviousCallNotMoneyContract.into())
     }
 
     // Newly created coin for this call is in the output. Here we gather it,

+ 5 - 2
src/contract/consensus/tests/stake_unstake.rs

@@ -130,6 +130,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
         token_blind: alice_money_stake_params.token_blind,
         nullifier: alice_money_stake_params.input.nullifier,
         merkle_root: alice_money_stake_params.input.merkle_root,
+        signature_public: alice_money_stake_params.input.signature_public,
         mint_zkbin: consensus_mint_zkbin.clone(),
         mint_pk: consensus_mint_pk.clone(),
     }
@@ -151,8 +152,9 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
     let calls = vec![money_call, consensus_call];
     let proofs = vec![alice_money_stake_proofs, alice_consensus_stake_proofs];
     let mut alice_stake_tx = Transaction { calls, proofs, signatures: vec![] };
-    let sigs = alice_stake_tx.create_sigs(&mut OsRng, &[alice_money_stake_secret_key])?;
-    alice_stake_tx.signatures = vec![sigs];
+    let money_sigs = alice_stake_tx.create_sigs(&mut OsRng, &[alice_money_stake_secret_key])?;
+    let consensus_sigs = alice_stake_tx.create_sigs(&mut OsRng, &[alice_money_stake_secret_key])?;
+    alice_stake_tx.signatures = vec![money_sigs, consensus_sigs];
 
     info!(target: "consensus", "[Faucet] ========================");
     info!(target: "consensus", "[Faucet] Executing Alice stake tx");
@@ -220,6 +222,7 @@ async fn consensus_contract_stake_unstake() -> Result<()> {
         token_blind: alice_consensus_unstake_params.token_blind,
         nullifier: alice_consensus_unstake_params.input.nullifier,
         merkle_root: alice_consensus_unstake_params.input.merkle_root,
+        signature_public: alice_consensus_unstake_params.input.signature_public,
         mint_zkbin: mint_zkbin.clone(),
         mint_pk: mint_pk.clone(),
     }

+ 3 - 0
src/contract/money/src/client/unstake_v1.rs

@@ -87,6 +87,8 @@ pub struct MoneyUnstakeCallBuilder {
     pub nullifier: Nullifier,
     /// Revealed Merkle root
     pub merkle_root: MerkleNode,
+    /// Public key for the signature
+    pub signature_public: PublicKey,
     /// `Mint_V1` zkas circuit ZkBinary
     pub mint_zkbin: ZkBinary,
     /// Proving key for the `Mint_V1` zk circuit
@@ -152,6 +154,7 @@ impl MoneyUnstakeCallBuilder {
             value_commit: public_inputs.value_commit,
             nullifier: self.nullifier,
             merkle_root: self.merkle_root,
+            signature_public: self.signature_public,
         };
 
         // We now fill this with necessary stuff

+ 10 - 2
src/contract/money/src/error.rs

@@ -80,9 +80,15 @@ pub enum MoneyError {
     #[error("Missing spend hook")]
     StakeMissingSpendHook,
 
+    #[error("Missing nullifier")]
+    StakeMissingNullifier,
+
     #[error("Next contract call is not consensus contract")]
     StakeNextCallNotConsensusContract,
 
+    #[error("Previous contract call is not money contract")]
+    StakePreviousCallNotMoneyContract,
+
     #[error("Spend hook is not money contract")]
     UnstakeSpendHookNotMoneyContract,
 }
@@ -110,8 +116,10 @@ impl From<MoneyError> for ContractError {
             MoneyError::MintFrozen => Self::Custom(18),
             MoneyError::StakeInputNonNativeToken => Self::Custom(19),
             MoneyError::StakeMissingSpendHook => Self::Custom(20),
-            MoneyError::StakeNextCallNotConsensusContract => Self::Custom(21),
-            MoneyError::UnstakeSpendHookNotMoneyContract => Self::Custom(21),
+            MoneyError::StakeMissingNullifier => Self::Custom(21),
+            MoneyError::StakeNextCallNotConsensusContract => Self::Custom(22),
+            MoneyError::StakePreviousCallNotMoneyContract => Self::Custom(23),
+            MoneyError::UnstakeSpendHookNotMoneyContract => Self::Custom(24),
         }
     }
 }

+ 2 - 0
src/contract/money/src/model.rs

@@ -71,6 +71,8 @@ pub struct StakeInput {
     pub nullifier: Nullifier,
     /// Revealed Merkle root
     pub merkle_root: MerkleNode,
+    /// Public key for the signature
+    pub signature_public: PublicKey,
 }
 
 /// A contract call's anonymous output