Przeglądaj źródła

contract/money: strictly constrain minted coin in auth token mint

skoupidi 2 miesięcy temu
rodzic
commit
90a9d62c22

+ 34 - 7
src/contract/money/proof/auth_token_mint_v1.zk

@@ -1,4 +1,4 @@
-# Circuit used to verify a token mint authority.
+# Circuit used to verify a token mint was executed by its authority.
 k = 11;
 field = "pallas";
 
@@ -7,25 +7,52 @@ constant "AuthTokenMint_V1" {
 }
 
 witness "AuthTokenMint_V1" {
+    # Secret key used by the mint authority
+    Base mint_secret,
+
     # TokenAttributes {
-    Base token_auth_parent,
+    Base token_auth_function,
     Base token_blind,
     # }
 
-    # Secret key used by the mint authority
-    Base mint_secret,
+    # CoinAttributes {
+    Base coin_public_x,
+    Base coin_public_y,
+    Base coin_value,
+    Base coin_spend_hook,
+    Base coin_user_data,
+    Base coin_blind,
+    # }
 }
 
 circuit "AuthTokenMint_V1" {
-    # Derive public key for the mint authority
+    # Derive and constrain the public key of the mint authority for the
+    # signature.
     mint_public = ec_mul_base(mint_secret, NULLIFIER_K);
     mint_x = ec_get_x(mint_public);
     mint_y = ec_get_y(mint_public);
     constrain_instance(mint_x);
     constrain_instance(mint_y);
 
-    # Derive the token ID
+    # Constrain the token authority function
+    constrain_instance(token_auth_function);
+
+    # Derive and constrain the token ID
     token_user_data = poseidon_hash(mint_x, mint_y);
-    token_id = poseidon_hash(token_auth_parent, token_user_data, token_blind);
+    token_id = poseidon_hash(token_auth_function, token_user_data, token_blind);
     constrain_instance(token_id);
+
+    # Derive and constrain the minted coin
+    coin = poseidon_hash(
+        coin_public_x,
+        coin_public_y,
+        coin_value,
+        token_id,
+        coin_spend_hook,
+        coin_user_data,
+        coin_blind,
+    );
+    constrain_instance(coin);
+
+    # At this point we've enforced all of our public inputs.
 }

+ 24 - 10
src/contract/money/src/client/auth_token_mint_v1.rs

@@ -21,7 +21,10 @@ use darkfi::{
     zkas::ZkBinary,
     Result,
 };
-use darkfi_sdk::crypto::{note::AeadEncryptedNote, Blind, Keypair};
+use darkfi_sdk::{
+    crypto::{note::AeadEncryptedNote, Blind, Keypair},
+    pasta::pallas,
+};
 use rand::rngs::OsRng;
 use tracing::debug;
 
@@ -54,18 +57,33 @@ impl AuthTokenMintCallBuilder {
         debug!(target: "contract::money::client::auth_token_mint", "Building Money::AuthTokenMintV1 contract call");
 
         // Create the proof
+        let (public_x, public_y) = self.coin_attrs.public_key.xy();
         let prover_witnesses = vec![
+            // Secret key used by the mint authority
+            Witness::Base(Value::known(self.mint_keypair.secret.inner())),
             // Token attributes
             Witness::Base(Value::known(self.token_attrs.auth_parent.inner())),
             Witness::Base(Value::known(self.token_attrs.blind.inner())),
-            // Secret key used by the mint authority
-            Witness::Base(Value::known(self.mint_keypair.secret.inner())),
+            // Coin attributes
+            Witness::Base(Value::known(public_x)),
+            Witness::Base(Value::known(public_y)),
+            Witness::Base(Value::known(pallas::Base::from(self.coin_attrs.value))),
+            Witness::Base(Value::known(self.coin_attrs.spend_hook.inner())),
+            Witness::Base(Value::known(self.coin_attrs.user_data)),
+            Witness::Base(Value::known(self.coin_attrs.blind.inner())),
         ];
 
         let mint_pubkey = self.mint_keypair.public;
+        let token_id = self.token_attrs.to_token_id();
+        let coin = self.coin_attrs.to_coin();
 
-        let public_inputs =
-            vec![mint_pubkey.x(), mint_pubkey.y(), self.token_attrs.to_token_id().inner()];
+        let public_inputs = vec![
+            mint_pubkey.x(),
+            mint_pubkey.y(),
+            self.token_attrs.auth_parent.inner(),
+            token_id.inner(),
+            coin.inner(),
+        ];
 
         //darkfi::zk::export_witness_json("proof/witness/auth_token_mint_v1.json", &prover_witnesses, &public_inputs);
         let circuit = ZkCircuit::new(prover_witnesses, &self.auth_mint_zkbin);
@@ -85,11 +103,7 @@ impl AuthTokenMintCallBuilder {
 
         let enc_note = AeadEncryptedNote::encrypt(&note, &self.coin_attrs.public_key, &mut OsRng)?;
 
-        let params = MoneyAuthTokenMintParamsV1 {
-            token_id: self.token_attrs.to_token_id(),
-            enc_note,
-            mint_pubkey,
-        };
+        let params = MoneyAuthTokenMintParamsV1 { token_id, enc_note, mint_pubkey };
         let debris = AuthTokenMintCallDebris { params, proofs: vec![proof] };
         Ok(debris)
     }

+ 35 - 4
src/contract/money/src/entrypoint/auth_token_mint_v1.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::{
-    crypto::{ContractId, PublicKey},
+    crypto::{ContractId, FuncRef, PublicKey},
     dark_tree::DarkLeaf,
     error::{ContractError, ContractResult},
     msg,
@@ -28,7 +28,8 @@ use darkfi_serial::{deserialize, serialize, Encodable};
 
 use crate::{
     error::MoneyError,
-    model::{MoneyAuthTokenMintParamsV1, MoneyAuthTokenMintUpdateV1},
+    model::{MoneyAuthTokenMintParamsV1, MoneyAuthTokenMintUpdateV1, MoneyTokenMintParamsV1},
+    MoneyFunction::TokenMintV1,
     MONEY_CONTRACT_TOKEN_FREEZE_TREE, MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1,
 };
 
@@ -38,16 +39,36 @@ pub(crate) fn money_auth_token_mint_get_metadata_v1(
     call_idx: usize,
     calls: Vec<DarkLeaf<ContractCall>>,
 ) -> Result<Vec<u8>, ContractError> {
-    let params: MoneyAuthTokenMintParamsV1 = deserialize(&calls[call_idx].data.data[1..])?;
+    let self_ = &calls[call_idx].data;
+    let params: MoneyAuthTokenMintParamsV1 = deserialize(&self_.data[1..])?;
 
     // 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 signature_pubkeys: Vec<PublicKey> = vec![params.mint_pubkey];
 
+    // Grab the mint authority pubkey coords
+    let (mint_x, mint_y) = params.mint_pubkey.xy();
+
+    // Derive our function ID
+    let func_id = FuncRef { contract_id: self_.contract_id, func_code: self_.data[0] }.to_func_id();
+
+    // Retrieve the minted coin params from the parent call
+    let parent_idx = calls[call_idx].parent_index.unwrap();
+    let coin_mint_call = &calls[parent_idx].data;
+    let coin_mint_params: MoneyTokenMintParamsV1 = deserialize(&coin_mint_call.data[1..])?;
+
+    // In ZK we verify that the token ID is properly derived from the
+    // authority and the minted coin corresponds to this mint proof.
     zk_public_inputs.push((
         MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1.to_string(),
-        vec![params.mint_pubkey.x(), params.mint_pubkey.y(), params.token_id.inner()],
+        vec![
+            mint_x,
+            mint_y,
+            func_id.inner(),
+            params.token_id.inner(),
+            coin_mint_params.coin.inner(),
+        ],
     ));
 
     // Serialize everything gathered and return it
@@ -67,6 +88,16 @@ pub(crate) fn money_auth_token_mint_process_instruction_v1(
     let self_ = &calls[call_idx].data;
     let params: MoneyAuthTokenMintParamsV1 = deserialize(&self_.data[1..])?;
 
+    // Ensure parent call is token mint
+    let parent_idx = calls[call_idx].parent_index.unwrap();
+    let coin_mint_call = &calls[parent_idx].data;
+    if coin_mint_call.contract_id != self_.contract_id ||
+        coin_mint_call.data[0] != TokenMintV1 as u8
+    {
+        msg!("[AuthTokenMintV1] Error: Parent call is missing");
+        return Err(MoneyError::ParentCallFunctionMismatch.into())
+    }
+
     // We have to check if the token mint is frozen.
     let token_freeze_db = wasm::db::db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?;
 

+ 2 - 2
src/contract/test-harness/src/vks.rs

@@ -49,8 +49,8 @@ use tracing::debug;
 
 /// Update these if any circuits are changed.
 /// Delete the existing cachefiles, and enable debug logging, you will see the new hashes.
-const PKS_HASH: &str = "76c13b755770efd5d2fd4f7f635ab5c20366686482ab59f5e7ec7315ee2b8a82";
-const VKS_HASH: &str = "917098f91e004cd28fe12e51a4204b7d3ee676a9dd8bb89e5a935c9d2f55ca99";
+const PKS_HASH: &str = "f0788d23abe5afe9778f3856c6a816619e8943028c149213d4ebcb502678b87e";
+const VKS_HASH: &str = "106e8ac76f003740d5e16ef1fcf74dee068cdc238a6bb7a0a7832ef3bf863911";
 
 /// Build a `PathBuf` to a cachefile
 fn cache_path(typ: &str) -> Result<PathBuf> {