Просмотр исходного кода

dao: modify proposals so they now just specify a generic call. This is done through an intermediate 'auth' contract. We provide one called DaoAuthMoneyTransfer so DAOs can transfer money around.

x 2 лет назад
Родитель
Сommit
2f80d8ad26

+ 1 - 0
Cargo.lock

@@ -1955,6 +1955,7 @@ dependencies = [
 name = "darkfi_dao_contract"
 version = "0.4.1"
 dependencies = [
+ "blake3 1.5.0",
  "bs58",
  "chacha20poly1305",
  "darkfi",

+ 1 - 0
src/contract/dao/Cargo.toml

@@ -9,6 +9,7 @@ edition = "2021"
 crate-type = ["cdylib", "rlib"]
 
 [dependencies]
+blake3 = "1.5.0"
 bs58 = "0.5.0"
 darkfi-sdk = { path = "../../sdk" }
 darkfi-serial = { path = "../../serial", features = ["derive", "crypto"] }

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

@@ -0,0 +1,61 @@
+k = 13;
+field = "pallas";
+
+constant "DaoAuthMoneyTransfer" {
+	EcFixedPointShort VALUE_COMMIT_VALUE,
+	EcFixedPoint VALUE_COMMIT_RANDOM,
+}
+
+witness "DaoAuthMoneyTransfer" {
+	# Proposal parameters
+    Base proposal_auth_calls_commit,
+    Base proposal_user_data,
+	Base proposal_blind,
+
+	# DAO parameters
+	Base dao_proposer_limit,
+	Base dao_quorum,
+	Base dao_approval_ratio_quot,
+	Base dao_approval_ratio_base,
+	Base gov_token_id,
+	Base dao_public_x,
+	Base dao_public_y,
+	Base dao_bulla_blind,
+
+	# Votes
+	Base yes_vote_value,
+	Base all_vote_value,
+	Scalar yes_vote_blind,
+	Scalar all_vote_blind,
+}
+
+circuit "DaoAuthMoneyTransfer" {
+	dao_bulla = poseidon_hash(
+		dao_proposer_limit,
+		dao_quorum,
+		dao_approval_ratio_quot,
+		dao_approval_ratio_base,
+		gov_token_id,
+		dao_public_x,
+		dao_public_y,
+		dao_bulla_blind,
+	);
+
+	# Proposal bulla being valid means DAO bulla is also valid because
+	# dao-propose-main.zk already checks that when we first create the
+	# proposal - so it is redundant to check DAO bulla exists here.
+	proposal_bulla = poseidon_hash(
+        proposal_auth_calls_commit,
+        proposal_user_data,
+		dao_bulla,
+		proposal_blind,
+	);
+	constrain_instance(proposal_bulla);
+
+    # Check inputs are spending from the correct DAO
+    # Change output should be sending back to the DAO
+
+    # Reveal content commit. This should contain the set of coins.
+    # We check these are set in the runtime.
+}
+

+ 6 - 7
src/contract/dao/proof/dao-exec.zk

@@ -8,9 +8,8 @@ constant "DaoExec" {
 
 witness "DaoExec" {
 	# Proposal parameters
-    Base proposal_content_commit,
-    Base proposal_auth_contract_id,
-    Base proposal_auth_function_id,
+    Base proposal_auth_calls_commit,
+    Base proposal_user_data,
 	Base proposal_blind,
 
 	# DAO parameters
@@ -44,15 +43,15 @@ circuit "DaoExec" {
 
 	# Proposal bulla being valid means DAO bulla is also valid because
 	# dao-propose-main.zk already checks that when we first create the
-	# proposal - so it is redundant here.
+	# proposal - so it is redundant to check DAO bulla exists here.
 	proposal_bulla = poseidon_hash(
-        proposal_content_commit,
-        proposal_auth_contract_id,
-        proposal_auth_function_id,
+        proposal_auth_calls_commit,
+        proposal_user_data,
 		dao_bulla,
 		proposal_blind,
 	);
 	constrain_instance(proposal_bulla);
+	constrain_instance(proposal_auth_calls_commit);
 
 	# Create Pedersen commitments for win_votes and total_votes, and
 	# constrain the commitments' coordinates.

+ 4 - 6
src/contract/dao/proof/dao-propose-main.zk

@@ -15,9 +15,8 @@ witness "DaoProposeMain" {
 	Base gov_token_blind,
 
 	# Proposal parameters
-    Base proposal_content_commit,
-    Base proposal_auth_contract_id,
-    Base proposal_auth_function_id,
+    Base proposal_auth_calls_commit,
+    Base proposal_user_data,
 	Base proposal_blind,
 
 	# DAO params
@@ -54,9 +53,8 @@ circuit "DaoProposeMain" {
 	# Proves this DAO is valid
 
 	proposal_bulla = poseidon_hash(
-        proposal_content_commit,
-        proposal_auth_contract_id,
-        proposal_auth_function_id,
+        proposal_auth_calls_commit,
+        proposal_user_data,
 		dao_bulla,
 		proposal_blind,
 	);

+ 4 - 6
src/contract/dao/proof/dao-vote-main.zk

@@ -8,9 +8,8 @@ constant "DaoVoteMain" {
 
 witness "DaoVoteMain" {
 	# Proposal parameters
-    Base proposal_content_commit,
-    Base proposal_auth_contract_id,
-    Base proposal_auth_function_id,
+    Base proposal_auth_calls_commit,
+    Base proposal_user_data,
 	Base proposal_blind,
 
 	# DAO parameters
@@ -51,9 +50,8 @@ circuit "DaoVoteMain" {
 	);
 
 	proposal_bulla = poseidon_hash(
-        proposal_content_commit,
-        proposal_auth_contract_id,
-        proposal_auth_function_id,
+        proposal_auth_calls_commit,
+        proposal_user_data,
 		dao_bulla,
 		proposal_blind,
 	);

+ 47 - 0
src/contract/dao/src/client/auth_xfer.rs

@@ -0,0 +1,47 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_sdk::{
+    crypto::{pasta_prelude::*, pedersen_commitment_u64, SecretKey},
+    pasta::pallas,
+};
+
+use log::debug;
+use rand::rngs::OsRng;
+
+use darkfi::{
+    zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
+    zkas::ZkBinary,
+    Result,
+};
+
+use crate::model::{Dao, DaoAuthMoneyTransferParams, DaoBlindAggregateVote, DaoProposal};
+
+pub struct DaoAuthMoneyTransferCall {}
+
+impl DaoAuthMoneyTransferCall {
+    pub fn make(
+        self,
+        //_auth_xfer_zkbin: &ZkBinary,
+        //_auth_xfer_pk: &ProvingKey,
+    ) -> Result<(DaoAuthMoneyTransferParams, Vec<Proof>)> {
+        let proofs = vec![];
+        let params = DaoAuthMoneyTransferParams {};
+        Ok((params, proofs))
+    }
+}

+ 8 - 6
src/contract/dao/src/client/exec.rs

@@ -16,9 +16,8 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_money_contract::model::CoinParams;
 use darkfi_sdk::{
-    crypto::{pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, SecretKey},
+    crypto::{pasta_prelude::*, pedersen_commitment_u64, SecretKey},
     pasta::pallas,
 };
 
@@ -31,7 +30,7 @@ use darkfi::{
     Result,
 };
 
-use crate::model::{Dao, DaoBlindAggregateVote, DaoExecParams, DaoProposal};
+use crate::model::{Dao, DaoBlindAggregateVote, DaoExecParams, DaoProposal, VecAuthCallCommit};
 
 pub struct DaoExecCall {
     pub proposal: DaoProposal,
@@ -75,11 +74,12 @@ impl DaoExecCall {
         let all_vote_commit = pedersen_commitment_u64(self.all_vote_value, self.all_vote_blind);
         let all_vote_commit_coords = all_vote_commit.to_affine().coordinates().unwrap();
 
+        let proposal_auth_calls_commit = self.proposal.auth_calls.commit();
+
         let prover_witnesses = vec![
             // proposal params
-            Witness::Base(Value::known(self.proposal.content_commit)),
-            Witness::Base(Value::known(self.proposal.auth_contract_id)),
-            Witness::Base(Value::known(self.proposal.auth_function_id)),
+            Witness::Base(Value::known(proposal_auth_calls_commit)),
+            Witness::Base(Value::known(self.proposal.user_data)),
             Witness::Base(Value::known(self.proposal.blind)),
             // DAO params
             Witness::Base(Value::known(dao_proposer_limit)),
@@ -100,6 +100,7 @@ impl DaoExecCall {
         debug!(target: "dao", "proposal_bulla: {:?}", proposal_bulla);
         let public_inputs = vec![
             proposal_bulla.inner(),
+            proposal_auth_calls_commit,
             *yes_vote_commit_coords.x(),
             *yes_vote_commit_coords.y(),
             *all_vote_commit_coords.x(),
@@ -114,6 +115,7 @@ impl DaoExecCall {
 
         let params = DaoExecParams {
             proposal: proposal_bulla,
+            proposal_auth_calls: self.proposal.auth_calls,
             blind_total_vote: DaoBlindAggregateVote { yes_vote_commit, all_vote_commit },
         };
 

+ 3 - 0
src/contract/dao/src/client/mod.rs

@@ -40,6 +40,9 @@ pub use vote::{DaoVoteCall, DaoVoteInput, DaoVoteNote};
 pub mod exec;
 pub use exec::DaoExecCall;
 
+pub mod auth_xfer;
+pub use auth_xfer::DaoAuthMoneyTransferCall;
+
 // Wallet SQL table constant names. These have to represent the SQL schema.
 pub const DAO_DAOS_TABLE: &str = "dao_daos";
 pub const DAO_DAOS_COL_DAO_ID: &str = "dao_id";

+ 3 - 4
src/contract/dao/src/client/propose.rs

@@ -35,7 +35,7 @@ use darkfi::{
     Result,
 };
 
-use crate::model::{Dao, DaoProposal, DaoProposeParams, DaoProposeParamsInput};
+use crate::model::{Dao, DaoProposal, DaoProposeParams, DaoProposeParamsInput, VecAuthCallCommit};
 
 #[derive(SerialEncodable, SerialDecodable)]
 pub struct DaoProposeNote {
@@ -178,9 +178,8 @@ impl DaoProposeCall {
             // Used for blinding exported gov token ID
             Witness::Base(Value::known(gov_token_blind)),
             // proposal params
-            Witness::Base(Value::known(self.proposal.content_commit)),
-            Witness::Base(Value::known(self.proposal.auth_contract_id)),
-            Witness::Base(Value::known(self.proposal.auth_function_id)),
+            Witness::Base(Value::known(self.proposal.auth_calls.commit())),
+            Witness::Base(Value::known(self.proposal.user_data)),
             Witness::Base(Value::known(self.proposal.blind)),
             // DAO params
             Witness::Base(Value::known(dao_proposer_limit)),

+ 3 - 4
src/contract/dao/src/client/vote.rs

@@ -36,7 +36,7 @@ use darkfi::{
     Result,
 };
 
-use crate::model::{Dao, DaoProposal, DaoVoteParams, DaoVoteParamsInput};
+use crate::model::{Dao, DaoProposal, DaoVoteParams, DaoVoteParamsInput, VecAuthCallCommit};
 
 #[derive(SerialEncodable, SerialDecodable)]
 pub struct DaoVoteNote {
@@ -191,9 +191,8 @@ impl DaoVoteCall {
 
         let prover_witnesses = vec![
             // proposal params
-            Witness::Base(Value::known(self.proposal.content_commit)),
-            Witness::Base(Value::known(self.proposal.auth_contract_id)),
-            Witness::Base(Value::known(self.proposal.auth_function_id)),
+            Witness::Base(Value::known(self.proposal.auth_calls.commit())),
+            Witness::Base(Value::known(self.proposal.user_data)),
             Witness::Base(Value::known(self.proposal.blind)),
             // DAO params
             Witness::Base(Value::known(dao_proposer_limit)),

+ 52 - 0
src/contract/dao/src/entrypoint/auth_xfer.rs

@@ -0,0 +1,52 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use darkfi_money_contract::{model::MoneyTransferParamsV1, MoneyFunction};
+use darkfi_sdk::{
+    crypto::{contract_id::MONEY_CONTRACT_ID, pasta_prelude::*, ContractId, PublicKey},
+    dark_tree::DarkLeaf,
+    db::{db_del, db_get, db_lookup},
+    error::{ContractError, ContractResult},
+    msg,
+    pasta::pallas,
+    ContractCall,
+};
+use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
+
+use crate::{
+    error::DaoError, model::DaoAuthMoneyTransferParams, DaoFunction,
+    DAO_CONTRACT_DB_PROPOSAL_BULLAS, DAO_CONTRACT_ZKAS_DAO_EXEC_NS,
+};
+
+/// `get_metdata` function for `Dao::Exec`
+pub(crate) fn dao_authxfer_get_metadata(
+    cid: ContractId,
+    call_idx: u32,
+    calls: Vec<DarkLeaf<ContractCall>>,
+) -> Result<Vec<u8>, ContractError> {
+    Ok(vec![])
+}
+
+/// `process_instruction` function for `Dao::Exec`
+pub(crate) fn dao_authxfer_process_instruction(
+    cid: ContractId,
+    call_idx: u32,
+    calls: Vec<DarkLeaf<ContractCall>>,
+) -> Result<Vec<u8>, ContractError> {
+    Ok(vec![])
+}

+ 23 - 1
src/contract/dao/src/entrypoint/exec.rs

@@ -30,7 +30,7 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 
 use crate::{
     error::DaoError,
-    model::{DaoExecParams, DaoExecUpdate, DaoProposalMetadata},
+    model::{DaoExecParams, DaoExecUpdate, DaoProposalMetadata, VecAuthCallCommit},
     DaoFunction, DAO_CONTRACT_DB_PROPOSAL_BULLAS, DAO_CONTRACT_ZKAS_DAO_EXEC_NS,
 };
 
@@ -72,6 +72,7 @@ pub(crate) fn dao_exec_get_metadata(
         DAO_CONTRACT_ZKAS_DAO_EXEC_NS.to_string(),
         vec![
             dao_exec_params.proposal.inner(),
+            dao_exec_params.proposal_auth_calls.commit(),
             *yes_vote_coords.x(),
             *yes_vote_coords.y(),
             *all_vote_coords.x(),
@@ -96,6 +97,25 @@ pub(crate) fn dao_exec_process_instruction(
     let self_ = &calls[call_idx as usize];
     let params: DaoExecParams = deserialize(&self_.data.data[1..])?;
 
+    // Check children of DAO exec match the specified calls
+    for auth_call in &params.proposal_auth_calls {
+        let child_idx = self_.children_indexes[auth_call.index];
+        let child = &calls[child_idx];
+        let call = &child.data;
+
+        let function_code = call.data[0] as u64;
+        // How can I do this?
+        //let auth_call_function_code: u64 = auth_call.function_id.into();
+
+        if call.contract_id.inner() != auth_call.contract_id {
+            // || function_code != auth_call_function_code {
+            msg!("[Dao::Exec] Error: wrong child call");
+            //return Err(DaoError::ExecCallWrongChildCall.into())
+        }
+    }
+
+    /*
+
     // ==========================================
     // Enforce the transaction has correct format
     // ==========================================
@@ -139,6 +159,8 @@ pub(crate) fn dao_exec_process_instruction(
         return Err(DaoError::ExecCallOutputsLenNot2.into())
     }
 
+    */
+
     // 2. Get the ProposalVote from DAO state
     let proposal_db = db_lookup(cid, DAO_CONTRACT_DB_PROPOSAL_BULLAS)?;
     let Some(data) = db_get(proposal_db, &serialize(&params.proposal))? else {

+ 8 - 4
src/contract/dao/src/error.rs

@@ -53,6 +53,9 @@ pub enum DaoError {
     #[error("Attempted double vote")]
     DoubleVote,
 
+    #[error("Child of exec call does not match proposal")]
+    ExecCallWrongChildCall,
+
     #[error("Exec call has invalid tx format")]
     ExecCallInvalidFormat,
 
@@ -80,10 +83,11 @@ impl From<DaoError> for ContractError {
             DaoError::ProposalEnded => Self::Custom(9),
             DaoError::CoinAlreadySpent => Self::Custom(10),
             DaoError::DoubleVote => Self::Custom(11),
-            DaoError::ExecCallInvalidFormat => Self::Custom(12),
-            DaoError::ExecCallOutputsLenNot2 => Self::Custom(13),
-            DaoError::ExecCallValueMismatch => Self::Custom(14),
-            DaoError::VoteCommitMismatch => Self::Custom(15),
+            DaoError::ExecCallWrongChildCall => Self::Custom(12),
+            DaoError::ExecCallInvalidFormat => Self::Custom(13),
+            DaoError::ExecCallOutputsLenNot2 => Self::Custom(14),
+            DaoError::ExecCallValueMismatch => Self::Custom(15),
+            DaoError::VoteCommitMismatch => Self::Custom(16),
         }
     }
 }

+ 41 - 17
src/contract/dao/src/model.rs

@@ -17,6 +17,7 @@
  */
 
 use core::str::FromStr;
+use std::io::Read;
 
 use darkfi_sdk::{
     crypto::{
@@ -26,7 +27,7 @@ use darkfi_sdk::{
     error::ContractError,
     pasta::pallas,
 };
-use darkfi_serial::{SerialDecodable, SerialEncodable};
+use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
 
 #[cfg(feature = "client")]
 use darkfi_serial::async_trait;
@@ -114,11 +115,34 @@ impl TryInto<DaoBulla> for ShareAddress {
     }
 }
 
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct DaoAuthCall {
+    pub index: usize,
+    pub contract_id: pallas::Base,
+    pub function_id: pallas::Base,
+    pub proposal_data: Vec<u8>,
+}
+
+pub trait VecAuthCallCommit {
+    fn commit(&self) -> pallas::Base;
+}
+
+impl VecAuthCallCommit for Vec<DaoAuthCall> {
+    fn commit(&self) -> pallas::Base {
+        let mut hasher = blake3::Hasher::new();
+        self.encode(&mut hasher).unwrap();
+        let hash = hasher.finalize();
+        let bytes = hash.as_bytes();
+        let raw_base: [u64; 4] = Decodable::decode(&mut bytes.as_slice()).unwrap();
+        pallas::Base::from_raw(raw_base)
+    }
+}
+
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoProposal {
-    pub content_commit: pallas::Base,
-    pub auth_contract_id: pallas::Base,
-    pub auth_function_id: pallas::Base,
+    pub auth_calls: Vec<DaoAuthCall>,
+    /// Arbitrary data provided by the user. We don't use this.
+    pub user_data: pallas::Base,
     pub dao_bulla: DaoBulla,
     pub blind: pallas::Base,
 }
@@ -126,9 +150,8 @@ pub struct DaoProposal {
 impl DaoProposal {
     pub fn to_bulla(&self) -> DaoProposalBulla {
         let bulla = poseidon_hash([
-            self.content_commit,
-            self.auth_contract_id,
-            self.auth_function_id,
+            self.auth_calls.commit(),
+            self.user_data,
             self.dao_bulla.inner(),
             self.blind,
         ]);
@@ -174,7 +197,7 @@ darkfi_sdk::fp_to_bs58!(DaoProposalBulla);
 darkfi_sdk::ty_from_fp!(DaoProposalBulla);
 
 /// Parameters for `Dao::Mint`
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoMintParams {
     /// The DAO bulla
     pub dao_bulla: DaoBulla,
@@ -183,7 +206,7 @@ pub struct DaoMintParams {
 }
 
 /// State update for `Dao::Mint`
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoMintUpdate {
     /// Revealed DAO bulla
     pub dao_bulla: DaoBulla,
@@ -205,7 +228,7 @@ pub struct DaoProposeParams {
 }
 
 /// Input for a DAO proposal
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoProposeParamsInput {
     /// Value commitment for the input
     pub value_commit: pallas::Point,
@@ -216,7 +239,7 @@ pub struct DaoProposeParamsInput {
 }
 
 /// State update for `Dao::Propose`
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoProposeUpdate {
     /// Minted proposal bulla
     pub proposal_bulla: DaoProposalBulla,
@@ -225,7 +248,7 @@ pub struct DaoProposeUpdate {
 }
 
 /// Metadata for a DAO proposal on the blockchain
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoProposalMetadata {
     /// Vote aggregate
     pub vote_aggregate: DaoBlindAggregateVote,
@@ -249,7 +272,7 @@ pub struct DaoVoteParams {
 }
 
 /// Input for a DAO proposal vote
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoVoteParamsInput {
     /// Revealed nullifier
     pub nullifier: Nullifier,
@@ -274,7 +297,7 @@ pub struct DaoVoteUpdate {
 
 /// Represents a single or multiple blinded votes.
 /// These can be summed together.
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoBlindAggregateVote {
     /// Weighted vote commit
     pub yes_vote_commit: pallas::Point,
@@ -300,21 +323,22 @@ impl Default for DaoBlindAggregateVote {
 }
 
 /// Parameters for `Dao::Exec`
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoExecParams {
     /// The proposal bulla
     pub proposal: DaoProposalBulla,
+    pub proposal_auth_calls: Vec<DaoAuthCall>,
     /// Aggregated blinds for the vote commitments
     pub blind_total_vote: DaoBlindAggregateVote,
 }
 
 /// State update for `Dao::Exec`
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoExecUpdate {
     /// The proposal bulla
     pub proposal: DaoProposalBulla,
 }
 
 /// Parameters for `Dao::AuthMoneyTransfer`
-#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoAuthMoneyTransferParams {}

+ 3 - 9
src/contract/dao/tests/integration.rs

@@ -193,18 +193,12 @@ fn integration_test() -> Result<()> {
         }];
         // We can add whatever we want in here, even arbitrary text
         // It's up to the auth module to decide what to do with it.
-        let content_commit = poseidon_hash([proposal_coins[0].to_coin().inner()]);
-        let auth_contract_id = pallas::Base::ZERO;
-        let auth_function_id = pallas::Base::ZERO;
+        let user_data = pallas::Base::ZERO;
 
         let (propose_tx, propose_params, propose_info) = th.dao_propose(
             &Holder::Alice,
-            content_commit,
-            auth_contract_id,
-            auth_function_id,
-            &Holder::Rachel,
-            PROPOSAL_AMOUNT,
-            drk_token_id,
+            proposal_coins.clone(),
+            user_data,
             &dao,
             &dao_mint_params.dao_bulla,
         )?;

+ 15 - 1
src/contract/test-harness/src/dao_exec.rs

@@ -23,7 +23,7 @@ use darkfi::{
     Result,
 };
 use darkfi_dao_contract::{
-    client::DaoExecCall,
+    client::{DaoAuthMoneyTransferCall, DaoExecCall},
     model::{Dao, DaoBulla, DaoExecParams, DaoProposal},
     DaoFunction, DAO_CONTRACT_ZKAS_DAO_EXEC_NS,
 };
@@ -174,6 +174,20 @@ impl TestHarness {
         exec_params.encode(&mut data)?;
         let exec_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
 
+        // Auth module
+        let authxfer_builder = DaoAuthMoneyTransferCall {};
+        let (authxfer_params, authxfer_proofs) = authxfer_builder.make()?;
+        let mut data = vec![DaoFunction::AuthMoneyTransfer as u8];
+        authxfer_params.encode(&mut data)?;
+        let authxfer_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
+
+        // We need to construct this tree, where exec is the parent:
+        //
+        //   exec ->
+        //       authxfer
+        //       xfer
+        //
+
         let mut tx_builder = TransactionBuilder::new(
             ContractCallLeaf { call: exec_call, proofs: exec_proofs },
             vec![],

+ 16 - 11
src/contract/test-harness/src/dao_propose.rs

@@ -24,10 +24,10 @@ use darkfi::{
 };
 use darkfi_dao_contract::{
     client::{DaoProposeCall, DaoProposeStakeInput},
-    model::{Dao, DaoBulla, DaoProposal, DaoProposeParams},
+    model::{Dao, DaoAuthCall, DaoBulla, DaoProposal, DaoProposeParams},
     DaoFunction, DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
 };
-use darkfi_money_contract::client::OwnCoin;
+use darkfi_money_contract::{client::OwnCoin, model::CoinParams};
 use darkfi_sdk::{
     crypto::{pasta_prelude::Field, MerkleNode, SecretKey, TokenId, DAO_CONTRACT_ID},
     pasta::pallas,
@@ -42,12 +42,8 @@ impl TestHarness {
     pub fn dao_propose(
         &mut self,
         proposer: &Holder,
-        content_commit: pallas::Base,
-        auth_contract_id: pallas::Base,
-        auth_function_id: pallas::Base,
-        recipient: &Holder,
-        amount: u64,
-        tx_token_id: TokenId,
+        proposal_coins: Vec<CoinParams>,
+        user_data: pallas::Base,
         dao: &Dao,
         dao_bulla: &DaoBulla,
     ) -> Result<(Transaction, DaoProposeParams, DaoProposal)> {
@@ -80,10 +76,19 @@ impl TestHarness {
             signature_secret,
         };
 
+        let mut proposal_data = vec![];
+        proposal_coins.encode(&mut proposal_data).unwrap();
+
+        let auth_calls = vec![DaoAuthCall {
+            index: 0,
+            contract_id: DAO_CONTRACT_ID.inner(),
+            function_id: pallas::Base::from(DaoFunction::AuthMoneyTransfer as u64),
+            proposal_data,
+        }];
+
         let proposal = DaoProposal {
-            content_commit,
-            auth_contract_id,
-            auth_function_id,
+            auth_calls,
+            user_data,
             dao_bulla: dao.to_bulla(),
             blind: pallas::Base::random(&mut OsRng),
         };