Преглед на файлове

use proper FuncIds to ref contract funcs such as spend_hook in money

zero преди 2 години
родител
ревизия
9d33a10a0b
променени са 34 файла, в които са добавени 153 реда и са изтрити 147 реда
  1. 6 2
      bin/zkrunner/zkrunner.py
  2. 3 3
      src/contract/dao/proof/dao-auth-money-transfer.zk
  3. 6 6
      src/contract/dao/src/client/auth_xfer.rs
  4. 2 2
      src/contract/dao/src/client/propose.rs
  5. 2 2
      src/contract/dao/src/client/vote.rs
  6. 5 2
      src/contract/dao/src/entrypoint/auth_xfer.rs
  7. 11 4
      src/contract/dao/tests/integration.rs
  8. 2 1
      src/contract/money/proof/fee_v1.zk
  9. 1 1
      src/contract/money/src/client/auth_token_mint_v1.rs
  10. 12 13
      src/contract/money/src/client/fee_v1.rs
  11. 5 3
      src/contract/money/src/client/genesis_mint_v1.rs
  12. 2 2
      src/contract/money/src/client/mod.rs
  13. 3 3
      src/contract/money/src/client/pow_reward_v1.rs
  14. 4 4
      src/contract/money/src/client/swap_v1.rs
  15. 1 1
      src/contract/money/src/client/token_mint_v1.rs
  16. 0 1
      src/contract/money/src/client/transfer_v1/builder.rs
  17. 3 3
      src/contract/money/src/client/transfer_v1/mod.rs
  18. 7 7
      src/contract/money/src/client/transfer_v1/proof.rs
  19. 1 0
      src/contract/money/src/entrypoint.rs
  20. 1 8
      src/contract/money/src/entrypoint/fee_v1.rs
  21. 3 5
      src/contract/money/src/entrypoint/swap_v1.rs
  22. 15 24
      src/contract/money/src/entrypoint/transfer_v1.rs
  23. 2 8
      src/contract/money/src/error.rs
  24. 4 8
      src/contract/money/src/model.rs
  25. 7 3
      src/contract/test-harness/src/dao_exec.rs
  26. 3 3
      src/contract/test-harness/src/lib.rs
  27. 3 3
      src/contract/test-harness/src/money_airdrop.rs
  28. 2 2
      src/contract/test-harness/src/money_genesis_mint.rs
  29. 2 2
      src/contract/test-harness/src/money_otc_swap.rs
  30. 2 2
      src/contract/test-harness/src/money_pow_reward.rs
  31. 3 3
      src/contract/test-harness/src/money_token.rs
  32. 2 2
      src/sdk/python/src/zkas.rs
  33. 5 1
      src/sdk/src/crypto/func_ref.rs
  34. 23 13
      src/zk/debug.rs

+ 6 - 2
bin/zkrunner/zkrunner.py

@@ -138,7 +138,7 @@ def main(witness_file, source_file, mock=False, trace=False):
             show_trace(zkbin.opcodes(), circuit.opvalues())
 
         print("Verifying ZK proof...")
-        proof.verify(verifying_key, instances)
+        verify_status = proof.verify(verifying_key, instances)
 
     # Otherwise, we'll simply run the MockProver:
     else:
@@ -146,7 +146,11 @@ def main(witness_file, source_file, mock=False, trace=False):
         proof = MockProver.run(zkbin.k(), circuit, instances)
 
         print("Verifying MockProver...")
-        proof.verify()
+        verify_status = proof.verify()
+
+    if not verify_status:
+        eprint("Proof failed to verify")
+        return -3
 
     print("Proof verified successfully!")
     return 0

+ 3 - 3
src/contract/dao/proof/dao-auth-money-transfer.zk

@@ -33,7 +33,7 @@ witness "DaoAuthMoneyTransfer" {
     Base dao_coin_blind,
 
     # Should be set to DAO_CONTRACT_ID
-    Base dao_contract_id,
+    Base dao_exec_func_id,
 
     # Epehemeral secret used for diffie-hellman shared secret derivation
     Base ephem_secret,
@@ -85,12 +85,12 @@ circuit "DaoAuthMoneyTransfer" {
         dao_value,
         # Likewise the token_id must match the other output coins.
         dao_token_id,
-        dao_contract_id,
+        dao_exec_func_id,
         dao_bulla,
         dao_coin_blind,
     );
     constrain_instance(dao_change_coin);
-    constrain_instance(dao_contract_id);
+    constrain_instance(dao_exec_func_id);
 
     # Reveal content commit. This should contain the set of coins.
     # We check these are set in the runtime.

+ 6 - 6
src/contract/dao/src/client/auth_xfer.rs

@@ -18,7 +18,7 @@
 
 use darkfi_money_contract::model::CoinAttributes;
 use darkfi_sdk::{
-    crypto::{note::ElGamalEncryptedNote, poseidon_hash, PublicKey, SecretKey, DAO_CONTRACT_ID},
+    crypto::{note::ElGamalEncryptedNote, poseidon_hash, PublicKey, SecretKey},
     pasta::pallas,
 };
 
@@ -67,7 +67,7 @@ impl DaoAuthMoneyTransferCall {
             let note = [
                 value_base,
                 coin_attrs.token_id.inner(),
-                coin_attrs.spend_hook,
+                coin_attrs.spend_hook.inner(),
                 coin_attrs.user_data,
                 coin_attrs.blind,
             ];
@@ -78,7 +78,7 @@ impl DaoAuthMoneyTransferCall {
                 Witness::EcNiPoint(Value::known(coin_attrs.public_key.inner())),
                 Witness::Base(Value::known(value_base)),
                 Witness::Base(Value::known(coin_attrs.token_id.inner())),
-                Witness::Base(Value::known(coin_attrs.spend_hook)),
+                Witness::Base(Value::known(coin_attrs.spend_hook.inner())),
                 Witness::Base(Value::known(coin_attrs.user_data)),
                 Witness::Base(Value::known(coin_attrs.blind)),
                 Witness::Base(Value::known(ephem_secret.inner())),
@@ -148,8 +148,8 @@ impl DaoAuthMoneyTransferCall {
             Witness::Base(Value::known(dao_change_value)),
             Witness::Base(Value::known(self.dao_coin_attrs.token_id.inner())),
             Witness::Base(Value::known(self.dao_coin_attrs.blind)),
-            // DAO_CONTRACT_ID
-            Witness::Base(Value::known(DAO_CONTRACT_ID.inner())),
+            // DAO::exec() func ID
+            Witness::Base(Value::known(self.dao_coin_attrs.spend_hook.inner())),
             // Encrypted change DAO output
             Witness::Base(Value::known(ephem_secret.inner())),
         ];
@@ -158,7 +158,7 @@ impl DaoAuthMoneyTransferCall {
             self.proposal.to_bulla().inner(),
             input_user_data_enc,
             self.dao_coin_attrs.to_coin().inner(),
-            DAO_CONTRACT_ID.inner(),
+            self.dao_coin_attrs.spend_hook.inner(),
             self.proposal.auth_calls.commit(),
             ephem_x,
             ephem_y,

+ 2 - 2
src/contract/dao/src/client/propose.rs

@@ -22,7 +22,7 @@ use darkfi_sdk::{
     bridgetree::Hashable,
     crypto::{
         note::AeadEncryptedNote, pasta_prelude::*, pedersen::pedersen_commitment_u64,
-        poseidon_hash, MerkleNode, Nullifier, PublicKey, SecretKey,
+        poseidon_hash, FuncId, MerkleNode, Nullifier, PublicKey, SecretKey,
     },
     pasta::pallas,
 };
@@ -99,7 +99,7 @@ impl DaoProposeCall {
                 public_key,
                 value: note.value,
                 token_id: note.token_id,
-                spend_hook: pallas::Base::ZERO,
+                spend_hook: FuncId::none(),
                 user_data: pallas::Base::ZERO,
                 blind: note.coin_blind,
             }

+ 2 - 2
src/contract/dao/src/client/vote.rs

@@ -22,7 +22,7 @@ use darkfi_sdk::{
     bridgetree::Hashable,
     crypto::{
         note::ElGamalEncryptedNote, pasta_prelude::*, pedersen_commitment_u64, poseidon_hash,
-        util::fv_mod_fp_unsafe, Keypair, MerkleNode, Nullifier, PublicKey, SecretKey,
+        util::fv_mod_fp_unsafe, FuncId, Keypair, MerkleNode, Nullifier, PublicKey, SecretKey,
     },
     pasta::pallas,
 };
@@ -124,7 +124,7 @@ impl DaoVoteCall {
                 public_key,
                 value: note.value,
                 token_id: note.token_id,
-                spend_hook: pallas::Base::ZERO,
+                spend_hook: FuncId::none(),
                 user_data: pallas::Base::ZERO,
                 blind: note.coin_blind,
             }

+ 5 - 2
src/contract/dao/src/entrypoint/auth_xfer.rs

@@ -21,7 +21,7 @@ use darkfi_money_contract::{
     MoneyFunction,
 };
 use darkfi_sdk::{
-    crypto::{ContractId, PublicKey, DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
+    crypto::{ContractId, FuncRef, PublicKey, DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
     dark_tree::DarkLeaf,
     error::ContractError,
     msg,
@@ -84,6 +84,9 @@ pub(crate) fn dao_authxfer_get_metadata(
     // Also check the coin in the change output
     let last_coin = xfer_params.outputs.last().unwrap().coin;
 
+    let spend_hook =
+        FuncRef { contract_id: *DAO_CONTRACT_ID, func_code: DaoFunction::Exec as u8 }.to_func_id();
+
     let (ephem_x, ephem_y) = self_params.dao_change_attrs.ephem_public.xy();
     zk_public_inputs.push((
         DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_NS.to_string(),
@@ -91,7 +94,7 @@ pub(crate) fn dao_authxfer_get_metadata(
             exec_params.proposal_bulla.inner(),
             input_user_data_enc,
             last_coin.inner(),
-            DAO_CONTRACT_ID.inner(),
+            spend_hook.inner(),
             exec_params.proposal_auth_calls.commit(),
             ephem_x,
             ephem_y,

+ 11 - 4
src/contract/dao/tests/integration.rs

@@ -18,7 +18,10 @@
 
 use darkfi::Result;
 use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
-use darkfi_dao_contract::model::{Dao, DaoBlindAggregateVote};
+use darkfi_dao_contract::{
+    model::{Dao, DaoBlindAggregateVote},
+    DaoFunction,
+};
 use darkfi_money_contract::{
     model::{CoinAttributes, TokenAttributes},
     MoneyFunction,
@@ -28,7 +31,7 @@ use darkfi_sdk::{
         pasta_prelude::*,
         pedersen_commitment_u64, poseidon_hash,
         util::{fp_mod_fv, fp_to_u64},
-        FuncRef, DAO_CONTRACT_ID, DARK_TOKEN_ID, MONEY_CONTRACT_ID,
+        FuncId, FuncRef, DAO_CONTRACT_ID, DARK_TOKEN_ID, MONEY_CONTRACT_ID,
     },
     pasta::pallas,
 };
@@ -121,10 +124,14 @@ fn integration_test() -> Result<()> {
         info!("Stage 2. Send Treasury token");
 
         info!("[Faucet] Building DAO airdrop tx");
+        let spend_hook =
+            FuncRef { contract_id: *DAO_CONTRACT_ID, func_code: DaoFunction::Exec as u8 }
+                .to_func_id();
+
         let (airdrop_tx, airdrop_params) = th.airdrop_native(
             DRK_TOKEN_SUPPLY,
             &Holder::Dao,
-            Some(DAO_CONTRACT_ID.inner()),           // spend_hook
+            Some(spend_hook),                        // spend_hook
             Some(dao_mint_params.dao_bulla.inner()), // user_data
         )?;
 
@@ -240,7 +247,7 @@ fn integration_test() -> Result<()> {
             public_key: th.holders.get(&Holder::Rachel).unwrap().keypair.public,
             value: PROPOSAL_AMOUNT,
             token_id: drk_token_id,
-            spend_hook: pallas::Base::ZERO,
+            spend_hook: FuncId::none(),
             user_data: pallas::Base::ZERO,
             blind: pallas::Base::random(&mut OsRng),
         }];

+ 2 - 1
src/contract/money/proof/fee_v1.zk

@@ -83,7 +83,8 @@ circuit "Fee_V1" {
     constrain_instance(user_data_enc);
 
     # Reveal spend_hook
-    constrain_instance(input_spend_hook);
+    ZERO = witness_base(0);
+    constrain_equal_base(input_spend_hook, ZERO);
 
     # Derive a public key for the signature and
     # constrain its coordinates

+ 1 - 1
src/contract/money/src/client/auth_token_mint_v1.rs

@@ -68,7 +68,7 @@ impl AuthTokenMintCallBuilder {
             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)),
+            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)),
             // Token attributes

+ 12 - 13
src/contract/money/src/client/fee_v1.rs

@@ -31,7 +31,7 @@ use darkfi_sdk::{
     crypto::{
         note::AeadEncryptedNote,
         pasta_prelude::{Curve, CurveAffine, Field},
-        pedersen_commitment_u64, poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier,
+        pedersen_commitment_u64, poseidon_hash, FuncId, Keypair, MerkleNode, MerkleTree, Nullifier,
         PublicKey, SecretKey, DARK_TOKEN_ID,
     },
     pasta::pallas,
@@ -68,9 +68,9 @@ pub async fn append_fee_call(
     verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
 ) -> Result<(MoneyFeeParamsV1, FeeCallSecrets)> {
     assert!(coin.note.value > 0);
-    assert!(coin.note.token_id == *DARK_TOKEN_ID);
-    assert!(coin.note.user_data == pallas::Base::ZERO);
-    assert!(coin.note.spend_hook == pallas::Base::ZERO);
+    assert_eq!(coin.note.token_id, *DARK_TOKEN_ID);
+    assert_eq!(coin.note.user_data, pallas::Base::ZERO);
+    assert_eq!(coin.note.spend_hook, FuncId::none());
 
     // First we will verify the fee-less transaction to see how much gas
     // it uses for execution and verification.
@@ -106,7 +106,7 @@ pub async fn append_fee_call(
         public_key: keypair.public,
         value: change_value,
         token_id: coin.note.token_id,
-        spend_hook: pallas::Base::ZERO,
+        spend_hook: FuncId::none(),
         user_data: pallas::Base::ZERO,
         blind: pallas::Base::random(&mut OsRng),
     };
@@ -154,7 +154,6 @@ pub async fn append_fee_call(
             token_commit: public_inputs.token_commit,
             nullifier: public_inputs.nullifier,
             merkle_root: public_inputs.merkle_root,
-            spend_hook: public_inputs.input_spend_hook,
             user_data_enc: public_inputs.input_user_data_enc,
             signature_public: public_inputs.signature_public,
         },
@@ -200,10 +199,10 @@ pub struct FeeRevealed {
     pub token_commit: pallas::Base,
     /// Merkle root for input coin
     pub merkle_root: MerkleNode,
+    /// Input's spend hook
+    pub input_spend_hook: FuncId,
     /// Encrypted user data for input coin
     pub input_user_data_enc: pallas::Base,
-    /// Input's spend hook
-    pub input_spend_hook: pallas::Base,
     /// Public key used to sign transaction
     pub signature_public: PublicKey,
     /// Output coin commitment
@@ -229,7 +228,7 @@ impl FeeRevealed {
             self.token_commit,
             self.merkle_root.inner(),
             self.input_user_data_enc,
-            self.input_spend_hook,
+            self.input_spend_hook.inner(),
             *sigpub_coords.x(),
             *sigpub_coords.y(),
             self.output_coin.inner(),
@@ -258,7 +257,7 @@ fn create_fee_proof(
     input_value_blind: pallas::Scalar,
     output: &FeeCallOutput,
     output_value_blind: pallas::Scalar,
-    output_spend_hook: pallas::Base,
+    output_spend_hook: FuncId,
     output_user_data: pallas::Base,
     output_coin_blind: pallas::Base,
     token_blind: pallas::Base,
@@ -316,8 +315,8 @@ fn create_fee_proof(
         input_value_commit,
         token_commit,
         merkle_root,
-        input_user_data_enc,
         input_spend_hook: input.note.spend_hook,
+        input_user_data_enc,
         signature_public,
         output_coin,
         output_value_commit,
@@ -330,12 +329,12 @@ fn create_fee_proof(
         Witness::Base(Value::known(signature_secret.inner())),
         Witness::Base(Value::known(pallas::Base::from(input.note.value))),
         Witness::Scalar(Value::known(input_value_blind)),
-        Witness::Base(Value::known(input.note.spend_hook)),
+        Witness::Base(Value::known(input.note.spend_hook.inner())),
         Witness::Base(Value::known(input.note.user_data)),
         Witness::Base(Value::known(input.note.coin_blind)),
         Witness::Base(Value::known(input.user_data_blind)),
         Witness::Base(Value::known(pallas::Base::from(output.value))),
-        Witness::Base(Value::known(output_spend_hook)),
+        Witness::Base(Value::known(output_spend_hook.inner())),
         Witness::Base(Value::known(output_user_data)),
         Witness::Scalar(Value::known(output_value_blind)),
         Witness::Base(Value::known(output_coin_blind)),

+ 5 - 3
src/contract/money/src/client/genesis_mint_v1.rs

@@ -22,7 +22,9 @@ use darkfi::{
     Result,
 };
 use darkfi_sdk::{
-    crypto::{note::AeadEncryptedNote, pasta_prelude::*, Keypair, PublicKey, DARK_TOKEN_ID},
+    crypto::{
+        note::AeadEncryptedNote, pasta_prelude::*, FuncId, Keypair, PublicKey, DARK_TOKEN_ID,
+    },
     pasta::pallas,
 };
 use log::{debug, info};
@@ -66,7 +68,7 @@ pub struct GenesisMintCallBuilder {
     /// Amount of tokens we want to mint
     pub amount: u64,
     /// Spend hook for the output
-    pub spend_hook: pallas::Base,
+    pub spend_hook: FuncId,
     /// User data for the output
     pub user_data: pallas::Base,
     /// `Mint_V1` zkas circuit ZkBinary
@@ -94,7 +96,7 @@ impl GenesisMintCallBuilder {
             public_key: self.keypair.public,
             value: self.amount,
             token_id,
-            spend_hook: pallas::Base::ZERO,
+            spend_hook: FuncId::none(),
             user_data: pallas::Base::ZERO,
             blind: pallas::Base::random(&mut OsRng),
         };

+ 2 - 2
src/contract/money/src/client/mod.rs

@@ -28,7 +28,7 @@
 
 use darkfi_sdk::{
     bridgetree,
-    crypto::{Nullifier, SecretKey, TokenId},
+    crypto::{FuncId, Nullifier, SecretKey, TokenId},
     pasta::pallas,
 };
 use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
@@ -110,7 +110,7 @@ pub struct MoneyNote {
     pub token_id: TokenId,
     /// Spend hook used for protocol-owned liquidity.
     /// Specifies which contract owns this coin.
-    pub spend_hook: pallas::Base,
+    pub spend_hook: FuncId,
     /// User data used by protocol when spend hook is enabled
     pub user_data: pallas::Base,
     /// Blinding factor for the coin

+ 3 - 3
src/contract/money/src/client/pow_reward_v1.rs

@@ -24,7 +24,7 @@ use darkfi::{
 use darkfi_sdk::{
     blockchain::expected_reward,
     crypto::{
-        ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::*, PublicKey, SecretKey,
+        ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::*, FuncId, PublicKey, SecretKey,
         DARK_TOKEN_ID,
     },
     pasta::pallas,
@@ -77,7 +77,7 @@ pub struct PoWRewardCallBuilder {
     pub fork_previous_hash: blake3::Hash,
     /// Merkle tree of coins used to create inclusion proofs
     /// Spend hook for the output
-    pub spend_hook: pallas::Base,
+    pub spend_hook: FuncId,
     /// User data for the output
     pub user_data: pallas::Base,
     /// `Mint_V1` zkas circuit ZkBinary
@@ -100,7 +100,7 @@ impl PoWRewardCallBuilder {
             public_key: self.recipient,
             value,
             token_id,
-            spend_hook: pallas::Base::ZERO,
+            spend_hook: FuncId::none(),
             user_data: pallas::Base::ZERO,
             blind: pallas::Base::random(&mut OsRng),
         };

+ 4 - 4
src/contract/money/src/client/swap_v1.rs

@@ -25,7 +25,8 @@ use darkfi::{
 };
 use darkfi_sdk::{
     crypto::{
-        note::AeadEncryptedNote, pasta_prelude::*, MerkleTree, PublicKey, SecretKey, TokenId,
+        note::AeadEncryptedNote, pasta_prelude::*, FuncId, MerkleTree, PublicKey, SecretKey,
+        TokenId,
     },
     pasta::pallas,
 };
@@ -67,7 +68,7 @@ pub struct SwapCallBuilder {
     /// User data blind for the party's input
     pub user_data_blind_send: pallas::Base,
     /// Spend hook for the party's output
-    pub spend_hook_recv: pallas::Base,
+    pub spend_hook_recv: FuncId,
     /// User data for the party's output
     pub user_data_recv: pallas::Base,
     /// The blinds to be used for value pedersen commitments
@@ -122,7 +123,7 @@ impl SwapCallBuilder {
             public_key: self.pubkey,
             value: self.value_recv,
             token_id: self.token_id_recv,
-            spend_hook: pallas::Base::ZERO,
+            spend_hook: FuncId::none(),
             user_data: pallas::Base::ZERO,
             blind: pallas::Base::random(&mut OsRng),
         };
@@ -150,7 +151,6 @@ impl SwapCallBuilder {
             token_commit: public_inputs.token_commit,
             nullifier: public_inputs.nullifier,
             merkle_root: public_inputs.merkle_root,
-            spend_hook: public_inputs.spend_hook,
             user_data_enc: public_inputs.user_data_enc,
             signature_public: public_inputs.signature_public,
         });

+ 1 - 1
src/contract/money/src/client/token_mint_v1.rs

@@ -53,7 +53,7 @@ impl TokenMintCallBuilder {
             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)),
+            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)),
             // Token attributes

+ 0 - 1
src/contract/money/src/client/transfer_v1/builder.rs

@@ -125,7 +125,6 @@ impl TransferCallBuilder {
                 token_commit: public_inputs.token_commit,
                 nullifier: public_inputs.nullifier,
                 merkle_root: public_inputs.merkle_root,
-                spend_hook: public_inputs.spend_hook,
                 user_data_enc: public_inputs.user_data_enc,
                 signature_public: public_inputs.signature_public,
             });

+ 3 - 3
src/contract/money/src/client/transfer_v1/mod.rs

@@ -17,7 +17,7 @@
  */
 use darkfi::{zk::ProvingKey, zkas::ZkBinary, ClientFailed, Result};
 use darkfi_sdk::{
-    crypto::{pasta_prelude::*, Keypair, MerkleTree, PublicKey, TokenId},
+    crypto::{pasta_prelude::*, FuncId, Keypair, MerkleTree, PublicKey, TokenId},
     pasta::pallas,
 };
 use log::{debug, error};
@@ -126,7 +126,7 @@ pub fn make_transfer_call(
         public_key: recipient,
         value,
         token_id,
-        spend_hook: pallas::Base::ZERO,
+        spend_hook: FuncId::none(),
         user_data: pallas::Base::ZERO,
         blind: pallas::Base::random(&mut OsRng),
     });
@@ -136,7 +136,7 @@ pub fn make_transfer_call(
             public_key: keypair.public,
             value: change_value,
             token_id,
-            spend_hook: pallas::Base::ZERO,
+            spend_hook: FuncId::none(),
             user_data: pallas::Base::ZERO,
             blind: pallas::Base::random(&mut OsRng),
         });

+ 7 - 7
src/contract/money/src/client/transfer_v1/proof.rs

@@ -24,8 +24,8 @@ use darkfi::{
 use darkfi_sdk::{
     bridgetree::Hashable,
     crypto::{
-        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, MerkleNode, Nullifier, PublicKey,
-        SecretKey,
+        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, FuncId, MerkleNode, Nullifier,
+        PublicKey, SecretKey,
     },
     pasta::pallas,
 };
@@ -56,7 +56,7 @@ pub struct TransferBurnRevealed {
     pub token_commit: pallas::Base,
     pub nullifier: Nullifier,
     pub merkle_root: MerkleNode,
-    pub spend_hook: pallas::Base,
+    pub spend_hook: FuncId,
     pub user_data_enc: pallas::Base,
     pub signature_public: PublicKey,
 }
@@ -75,7 +75,7 @@ impl TransferBurnRevealed {
             self.token_commit,
             self.merkle_root.inner(),
             self.user_data_enc,
-            self.spend_hook,
+            self.spend_hook.inner(),
             *sigpub_coords.x(),
             *sigpub_coords.y(),
         ]
@@ -138,7 +138,7 @@ pub fn create_transfer_burn_proof(
         Witness::Base(Value::known(input.note.token_id.inner())),
         Witness::Scalar(Value::known(value_blind)),
         Witness::Base(Value::known(token_blind)),
-        Witness::Base(Value::known(input.note.spend_hook)),
+        Witness::Base(Value::known(input.note.spend_hook.inner())),
         Witness::Base(Value::known(input.note.user_data)),
         Witness::Base(Value::known(input.note.coin_blind)),
         Witness::Base(Value::known(input.user_data_blind)),
@@ -161,7 +161,7 @@ pub fn create_transfer_mint_proof(
     output: &TransferCallOutput,
     value_blind: pallas::Scalar,
     token_blind: pallas::Base,
-    spend_hook: pallas::Base,
+    spend_hook: FuncId,
     user_data: pallas::Base,
     coin_blind: pallas::Base,
 ) -> Result<(Proof, TransferMintRevealed)> {
@@ -187,7 +187,7 @@ pub fn create_transfer_mint_proof(
         Witness::Base(Value::known(pub_y)),
         Witness::Base(Value::known(pallas::Base::from(output.value))),
         Witness::Base(Value::known(output.token_id.inner())),
-        Witness::Base(Value::known(spend_hook)),
+        Witness::Base(Value::known(spend_hook.inner())),
         Witness::Base(Value::known(user_data)),
         Witness::Base(Value::known(coin_blind)),
         Witness::Scalar(Value::known(value_blind)),

+ 1 - 0
src/contract/money/src/entrypoint.rs

@@ -21,6 +21,7 @@ use darkfi_sdk::{
     dark_tree::DarkLeaf,
     db::{db_init, db_lookup, db_set, zkas_db_set},
     error::ContractResult,
+    msg,
     pasta::pallas,
     util::set_return_data,
     ContractCall,

+ 1 - 8
src/contract/money/src/entrypoint/fee_v1.rs

@@ -18,7 +18,7 @@
 
 use darkfi_sdk::{
     crypto::{
-        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, MerkleNode,
+        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, FuncId, MerkleNode,
         PublicKey, DARK_TOKEN_ID,
     },
     dark_tree::DarkLeaf,
@@ -68,7 +68,6 @@ pub(crate) fn money_fee_get_metadata_v1(
             params.input.token_commit,
             params.input.merkle_root.inner(),
             params.input.user_data_enc,
-            params.input.spend_hook,
             sig_x,
             sig_y,
             params.output.coin.inner(),
@@ -126,12 +125,6 @@ pub(crate) fn money_fee_process_instruction_v1(
         return Err(MoneyError::TokenMismatch.into())
     }
 
-    // The spend hook must be zero.
-    if params.input.spend_hook != pallas::Base::ZERO {
-        msg!("[FeeV1] Error: Input spend hook is nonzero");
-        return Err(MoneyError::SpendHookNonZero.into())
-    }
-
     // The Merkle root is used to know whether this is a coin that
     // existed in a previous state.
     if !db_contains_key(coin_roots_db, &serialize(&params.input.merkle_root))? {

+ 3 - 5
src/contract/money/src/entrypoint/swap_v1.rs

@@ -17,13 +17,11 @@
  */
 
 use darkfi_sdk::{
-    crypto::{pasta_prelude::*, ContractId},
+    crypto::{ContractId, FuncId},
     dark_tree::DarkLeaf,
     db::{db_contains_key, db_lookup},
     error::{ContractError, ContractResult},
-    msg,
-    pasta::pallas,
-    ContractCall,
+    msg, ContractCall,
 };
 use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 
@@ -113,7 +111,7 @@ pub(crate) fn money_otcswap_process_instruction_v1(
         // For now, make sure that the inputs' spend hooks are zero.
         // This should however be allowed to some extent, e.g. if we
         // want a DAO to be able to do an atomic swap.
-        if input.spend_hook != pallas::Base::ZERO {
+        if calls[call_idx as usize].parent_index.is_some() {
             msg!("[OtcSwapV1] Error: Unable to swap coins with spend_hook != 0 (input {})", i);
             return Err(MoneyError::SpendHookNonZero.into())
         }

+ 15 - 24
src/contract/money/src/entrypoint/transfer_v1.rs

@@ -18,8 +18,8 @@
 
 use darkfi_sdk::{
     crypto::{
-        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, MerkleNode,
-        PublicKey, DARK_TOKEN_ID,
+        pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, FuncId, FuncRef,
+        MerkleNode, PublicKey, DARK_TOKEN_ID,
     },
     dark_tree::DarkLeaf,
     db::{db_contains_key, db_get, db_lookup, db_set},
@@ -58,6 +58,18 @@ pub(crate) fn money_transfer_get_metadata_v1(
         signature_pubkeys.push(input.signature_public);
     }
 
+    // Calculate the spend hook
+    let spend_hook = match calls[call_idx as usize].parent_index {
+        Some(parent_idx) => {
+            let parent_call = &calls[parent_idx].data;
+            let contract_id = parent_call.contract_id;
+            let func_code = parent_call.data[0];
+
+            FuncRef { contract_id, func_code }.to_func_id()
+        }
+        None => FuncId::none(),
+    };
+
     // Grab the pedersen commitments and signature pubkeys from the
     // anonymous inputs
     for input in &params.inputs {
@@ -76,7 +88,7 @@ pub(crate) fn money_transfer_get_metadata_v1(
                 input.token_commit,
                 input.merkle_root.inner(),
                 input.user_data_enc,
-                input.spend_hook,
+                spend_hook.inner(),
                 sig_x,
                 sig_y,
             ],
@@ -187,27 +199,6 @@ pub(crate) fn money_transfer_process_instruction_v1(
             return Err(MoneyError::DuplicateNullifier.into())
         }
 
-        // If spend hook is set, check its correctness
-        if input.spend_hook != pallas::Base::ZERO {
-            let parent_call_idx = self_.parent_index;
-            if parent_call_idx.is_none() {
-                msg!("[TransferV1] Error: parent_call_idx is missing");
-                return Err(MoneyError::CallIdxOutOfBounds.into())
-            }
-            let parent_call_idx = parent_call_idx.unwrap();
-
-            if parent_call_idx >= calls.len() {
-                msg!("[TransferV1] Error: parent_call_idx out of bounds (input {})", i);
-                return Err(MoneyError::CallIdxOutOfBounds.into())
-            }
-
-            let parent = &calls[parent_call_idx].data;
-            if parent.contract_id.inner() != input.spend_hook {
-                msg!("[TransferV1] Error: Invoked contract call does not match spend hook in input {}", i);
-                return Err(MoneyError::SpendHookMismatch.into())
-            }
-        }
-
         // Append this new nullifier to seen nullifiers, and accumulate the value commitment
         new_nullifiers.push(input.nullifier);
         valcom_total += input.value_commit;

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

@@ -43,12 +43,6 @@ pub enum MoneyError {
     #[error("Duplicate nullifier found")]
     DuplicateNullifier,
 
-    #[error("Call index out of bounds")]
-    CallIdxOutOfBounds,
-
-    #[error("Spend hook mismatch")]
-    SpendHookMismatch,
-
     #[error("Duplicate coin found")]
     DuplicateCoin,
 
@@ -127,8 +121,8 @@ impl From<MoneyError> for ContractError {
             MoneyError::TransferClearInputUnauthorised => Self::Custom(5),
             MoneyError::TransferMerkleRootNotFound => Self::Custom(6),
             MoneyError::DuplicateNullifier => Self::Custom(7),
-            MoneyError::CallIdxOutOfBounds => Self::Custom(8),
-            MoneyError::SpendHookMismatch => Self::Custom(9),
+            // 8 was removed
+            // 9 was removed
             MoneyError::DuplicateCoin => Self::Custom(10),
             MoneyError::ValueMismatch => Self::Custom(11),
             MoneyError::TokenMismatch => Self::Custom(12),

+ 4 - 8
src/contract/money/src/model.rs

@@ -67,7 +67,7 @@ pub struct CoinAttributes {
     pub public_key: PublicKey,
     pub value: u64,
     pub token_id: TokenId,
-    pub spend_hook: pallas::Base,
+    pub spend_hook: FuncId,
     pub user_data: pallas::Base,
     /// Simultaneously blinds the coin and ensures uniqueness
     pub blind: pallas::Base,
@@ -82,7 +82,7 @@ impl CoinAttributes {
             pub_y,
             pallas::Base::from(self.value),
             self.token_id.inner(),
-            self.spend_hook,
+            self.spend_hook.inner(),
             self.user_data,
             self.blind,
         ]);
@@ -149,13 +149,9 @@ pub struct Input {
     pub nullifier: Nullifier,
     /// Revealed Merkle root
     pub merkle_root: MerkleNode,
-    /// Spend hook used to invoke other contracts.
-    /// If this value is nonzero then the subsequent contract call in the tx
-    /// must have this value as its ID.
-    pub spend_hook: pallas::Base,
     /// Encrypted user data field. An encrypted commitment to arbitrary data.
-    /// When spend hook is set (it is nonzero), then this field may be used
-    /// to pass data to the invoked contract.
+    /// When spend hook is nonzero, then this field may be used to pass data
+    /// to the invoked contract.
     pub user_data_enc: pallas::Base,
     /// Public key for the signature
     pub signature_public: PublicKey,

+ 7 - 3
src/contract/test-harness/src/dao_exec.rs

@@ -32,8 +32,8 @@ use darkfi_money_contract::{
 };
 use darkfi_sdk::{
     crypto::{
-        pasta_prelude::Field, pedersen_commitment_u64, MerkleNode, SecretKey, DAO_CONTRACT_ID,
-        MONEY_CONTRACT_ID,
+        pasta_prelude::Field, pedersen_commitment_u64, FuncRef, MerkleNode, SecretKey,
+        DAO_CONTRACT_ID, MONEY_CONTRACT_ID,
     },
     dark_tree::DarkLeaf,
     pasta::pallas,
@@ -115,11 +115,15 @@ impl TestHarness {
             outputs.push(coin_attr);
         }
 
+        let spend_hook =
+            FuncRef { contract_id: *DAO_CONTRACT_ID, func_code: DaoFunction::Exec as u8 }
+                .to_func_id();
+
         let dao_coin_attrs = CoinAttributes {
             public_key: dao_wallet.keypair.public,
             value: change_value,
             token_id: proposal_token_id,
-            spend_hook: DAO_CONTRACT_ID.inner(),
+            spend_hook,
             user_data: dao_bulla.inner(),
             blind: pallas::Base::random(&mut OsRng),
         };

+ 3 - 3
src/contract/test-harness/src/lib.rs

@@ -67,13 +67,13 @@ mod money_transfer;
 pub fn init_logger() {
     let mut cfg = simplelog::ConfigBuilder::new();
     cfg.add_filter_ignore("sled".to_string());
-    //cfg.set_target_level(simplelog::LevelFilter::Error);
+    cfg.set_target_level(simplelog::LevelFilter::Error);
 
     // We check this error so we can execute same file tests in parallel,
     // otherwise second one fails to init logger here.
     if simplelog::TermLogger::init(
-        simplelog::LevelFilter::Info,
-        //simplelog::LevelFilter::Debug,
+        //simplelog::LevelFilter::Info,
+        simplelog::LevelFilter::Debug,
         //simplelog::LevelFilter::Trace,
         cfg.build(),
         simplelog::TerminalMode::Mixed,

+ 3 - 3
src/contract/test-harness/src/money_airdrop.rs

@@ -29,7 +29,7 @@ use darkfi_money_contract::{
     MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{MerkleNode, DARK_TOKEN_ID, MONEY_CONTRACT_ID},
+    crypto::{FuncId, MerkleNode, DARK_TOKEN_ID, MONEY_CONTRACT_ID},
     pasta::pallas,
     ContractCall,
 };
@@ -44,7 +44,7 @@ impl TestHarness {
         &mut self,
         value: u64,
         holder: &Holder,
-        rcpt_spend_hook: Option<pallas::Base>,
+        rcpt_spend_hook: Option<FuncId>,
         rcpt_user_data: Option<pallas::Base>,
     ) -> Result<(Transaction, MoneyTransferParamsV1)> {
         let recipient = self.holders.get(holder).unwrap().keypair.public;
@@ -72,7 +72,7 @@ impl TestHarness {
                 public_key: recipient,
                 value,
                 token_id: *DARK_TOKEN_ID,
-                spend_hook: rcpt_spend_hook.unwrap_or(pallas::Base::ZERO),
+                spend_hook: rcpt_spend_hook.unwrap_or(FuncId::none()),
                 user_data: rcpt_user_data.unwrap_or(pallas::Base::ZERO),
                 blind: pallas::Base::random(&mut OsRng),
             }],

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

@@ -27,7 +27,7 @@ use darkfi_money_contract::{
     MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{MerkleNode, MONEY_CONTRACT_ID},
+    crypto::{FuncId, MerkleNode, MONEY_CONTRACT_ID},
     pasta::pallas,
     ContractCall,
 };
@@ -53,7 +53,7 @@ impl TestHarness {
         let timer = Instant::now();
 
         // We're just going to be using a zero spend-hook and user-data
-        let spend_hook = pallas::Base::zero();
+        let spend_hook = FuncId::none();
         let user_data = pallas::Base::zero();
 
         let builder = GenesisMintCallBuilder {

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

@@ -29,7 +29,7 @@ use darkfi_money_contract::{
     MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{MerkleNode, MONEY_CONTRACT_ID},
+    crypto::{FuncId, MerkleNode, MONEY_CONTRACT_ID},
     pasta::pallas,
     ContractCall,
 };
@@ -61,7 +61,7 @@ impl TestHarness {
         let timer = Instant::now();
 
         // We're just going to be using a zero spend-hook and user-data
-        let rcpt_spend_hook = pallas::Base::zero();
+        let rcpt_spend_hook = FuncId::none();
         let rcpt_user_data = pallas::Base::zero();
         let rcpt_user_data_blind = pallas::Base::random(&mut OsRng);
 

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

@@ -27,7 +27,7 @@ use darkfi_money_contract::{
     MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{MerkleNode, MONEY_CONTRACT_ID},
+    crypto::{FuncId, MerkleNode, MONEY_CONTRACT_ID},
     pasta::pallas,
     ContractCall,
 };
@@ -59,7 +59,7 @@ impl TestHarness {
         let fork_previous_hash = self.genesis_block.header.previous;
 
         // We're just going to be using a zero spend-hook and user-data
-        let spend_hook = pallas::Base::zero();
+        let spend_hook = FuncId::none();
         let user_data = pallas::Base::zero();
 
         let recipient = if let Some(holder) = recipient {

+ 3 - 3
src/contract/test-harness/src/money_token.rs

@@ -36,7 +36,7 @@ use darkfi_money_contract::{
     MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{poseidon_hash, FuncRef, MerkleNode, MONEY_CONTRACT_ID},
+    crypto::{poseidon_hash, FuncId, FuncRef, MerkleNode, MONEY_CONTRACT_ID},
     dark_tree::DarkLeaf,
     pasta::pallas,
     ContractCall,
@@ -52,7 +52,7 @@ impl TestHarness {
         amount: u64,
         holder: &Holder,
         recipient: &Holder,
-        spend_hook: Option<pallas::Base>,
+        spend_hook: Option<FuncId>,
         user_data: Option<pallas::Base>,
     ) -> Result<(Transaction, MoneyTokenMintParamsV1, MoneyAuthTokenMintParamsV1)> {
         let wallet = self.holders.get(holder).unwrap();
@@ -94,7 +94,7 @@ impl TestHarness {
             public_key: rcpt,
             value: amount,
             token_id,
-            spend_hook: spend_hook.unwrap_or(pallas::Base::ZERO),
+            spend_hook: spend_hook.unwrap_or(FuncId::none()),
             user_data: user_data.unwrap_or(pallas::Base::ZERO),
             blind: pallas::Base::random(&mut OsRng),
         };

+ 2 - 2
src/sdk/python/src/zkas.rs

@@ -280,10 +280,10 @@ impl Proof {
         Self(proof)
     }
 
-    fn verify(&self, vk: &PyCell<VerifyingKey>, instances: Vec<&PyCell<Fp>>) {
+    fn verify(&self, vk: &PyCell<VerifyingKey>, instances: Vec<&PyCell<Fp>>) -> bool {
         let vk = vk.borrow().deref().0.clone();
         let instances: Vec<pallas::Base> = instances.iter().map(|i| i.borrow().deref().0).collect();
-        self.0.verify(&vk, instances.as_slice()).unwrap();
+        self.0.verify(&vk, instances.as_slice()).is_ok()
     }
 }
 

+ 5 - 1
src/sdk/src/crypto/func_ref.rs

@@ -20,7 +20,7 @@ use darkfi_serial::async_trait;
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use pasta_curves::pallas;
 
-use super::{poseidon_hash, ContractId};
+use super::{pasta_prelude::*, poseidon_hash, ContractId};
 
 pub type FunctionCode = u8;
 
@@ -42,6 +42,10 @@ impl FuncRef {
 pub struct FuncId(pallas::Base);
 
 impl FuncId {
+    pub fn none() -> Self {
+        Self(pallas::Base::ZERO)
+    }
+
     pub fn inner(&self) -> pallas::Base {
         self.0
     }

+ 23 - 13
src/zk/debug.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_sdk::pasta::pallas;
+use darkfi_sdk::{crypto::pasta_prelude::*, pasta::pallas};
 use log::error;
 
 #[cfg(feature = "tinyjson")]
@@ -42,33 +42,43 @@ pub fn export_witness_json<P: AsRef<Path>>(
         let mut value_json = HashMap::new();
         match witness {
             Witness::Base(value) => {
-                value.map(|w1| {
-                    value_json.insert("Base".to_string(), JsonStr(format!("{:?}", w1)));
-                    w1
+                value.map(|w| {
+                    value_json.insert("Base".to_string(), JsonStr(format!("{:?}", w)));
+                    w
                 });
             }
             Witness::Scalar(value) => {
-                value.map(|w1| {
-                    value_json.insert("Scalar".to_string(), JsonStr(format!("{:?}", w1)));
-                    w1
+                value.map(|w| {
+                    value_json.insert("Scalar".to_string(), JsonStr(format!("{:?}", w)));
+                    w
                 });
             }
             Witness::Uint32(value) => {
-                value.map(|w1| {
-                    value_json.insert("Uint32".to_string(), JsonNum(w1.into()));
-                    w1
+                value.map(|w| {
+                    value_json.insert("Uint32".to_string(), JsonNum(w.into()));
+                    w
                 });
             }
             Witness::MerklePath(value) => {
                 let mut path = Vec::new();
-                value.map(|w1| {
-                    for node in w1 {
+                value.map(|w| {
+                    for node in w {
                         path.push(JsonStr(format!("{:?}", node.inner())));
                     }
-                    w1
+                    w
                 });
                 value_json.insert("MerklePath".to_string(), JsonArray(path));
             }
+            Witness::EcNiPoint(value) => {
+                let (mut x, mut y) = (pallas::Base::ZERO, pallas::Base::ZERO);
+                value.map(|w| {
+                    let coords = w.to_affine().coordinates().unwrap();
+                    (x, y) = (*coords.x(), *coords.y());
+                    w
+                });
+                let coords = vec![JsonStr(format!("{:?}", x)), JsonStr(format!("{:?}", y))];
+                value_json.insert("EcNiPoint".to_string(), JsonArray(coords));
+            }
             _ => unimplemented!(),
         }
         witnesses.push(JsonObj(value_json));