darkfi_dao_contract/client/
exec.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use darkfi_sdk::{
20    crypto::{pasta_prelude::*, pedersen_commitment_u64, PublicKey, ScalarBlind, SecretKey},
21    pasta::pallas,
22};
23
24use log::debug;
25use rand::rngs::OsRng;
26
27use darkfi::{
28    zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
29    zkas::ZkBinary,
30    ClientFailed, Result,
31};
32
33use crate::{
34    error::DaoError,
35    model::{Dao, DaoBlindAggregateVote, DaoExecParams, DaoProposal, VecAuthCallCommit},
36};
37
38pub struct DaoExecCall {
39    pub proposal: DaoProposal,
40    pub dao: Dao,
41    pub yes_vote_value: u64,
42    pub all_vote_value: u64,
43    pub yes_vote_blind: ScalarBlind,
44    pub all_vote_blind: ScalarBlind,
45    pub signature_secret: SecretKey,
46    pub current_blockwindow: u64,
47}
48
49impl DaoExecCall {
50    pub fn make(
51        self,
52        dao_exec_secret_key: &SecretKey,
53        dao_early_exec_secret_key: &Option<SecretKey>,
54        exec_zkbin: &ZkBinary,
55        exec_pk: &ProvingKey,
56    ) -> Result<(DaoExecParams, Vec<Proof>)> {
57        debug!(target: "contract::dao::client::exec", "build()");
58        let mut proofs = vec![];
59
60        let dao_proposer_limit = pallas::Base::from(self.dao.proposer_limit);
61        let dao_quorum = pallas::Base::from(self.dao.quorum);
62        let dao_early_exec_quorum = pallas::Base::from(self.dao.early_exec_quorum);
63        let dao_approval_ratio_quot = pallas::Base::from(self.dao.approval_ratio_quot);
64        let dao_approval_ratio_base = pallas::Base::from(self.dao.approval_ratio_base);
65        let (dao_notes_pub_x, dao_notes_pub_y) = self.dao.notes_public_key.xy();
66        let (dao_proposer_pub_x, dao_proposer_pub_y) = self.dao.proposer_public_key.xy();
67        let (dao_proposals_pub_x, dao_proposals_pub_y) = self.dao.proposals_public_key.xy();
68        let (dao_votes_pub_x, dao_votes_pub_y) = self.dao.votes_public_key.xy();
69
70        let dao_bulla = self.dao.to_bulla();
71        if dao_bulla != self.proposal.dao_bulla {
72            return Err(ClientFailed::VerifyError(DaoError::InvalidCalls.to_string()).into())
73        }
74        let proposal_bulla = self.proposal.to_bulla();
75
76        let yes_vote_commit = pedersen_commitment_u64(self.yes_vote_value, self.yes_vote_blind);
77        let yes_vote_commit_coords = yes_vote_commit.to_affine().coordinates().unwrap();
78
79        let all_vote_commit = pedersen_commitment_u64(self.all_vote_value, self.all_vote_blind);
80        let all_vote_commit_coords = all_vote_commit.to_affine().coordinates().unwrap();
81
82        let proposal_auth_calls_commit = self.proposal.auth_calls.commit();
83
84        let signature_public = PublicKey::from_secret(self.signature_secret);
85
86        let current_blockwindow = pallas::Base::from(self.current_blockwindow);
87
88        let mut prover_witnesses = vec![
89            // Proposal params
90            Witness::Base(Value::known(proposal_auth_calls_commit)),
91            Witness::Base(Value::known(pallas::Base::from(self.proposal.creation_blockwindow))),
92            Witness::Base(Value::known(pallas::Base::from(self.proposal.duration_blockwindows))),
93            Witness::Base(Value::known(self.proposal.user_data)),
94            Witness::Base(Value::known(self.proposal.blind.inner())),
95            // DAO params
96            Witness::Base(Value::known(dao_proposer_limit)),
97            Witness::Base(Value::known(dao_quorum)),
98            Witness::Base(Value::known(dao_early_exec_quorum)),
99            Witness::Base(Value::known(dao_approval_ratio_quot)),
100            Witness::Base(Value::known(dao_approval_ratio_base)),
101            Witness::Base(Value::known(self.dao.gov_token_id.inner())),
102            Witness::Base(Value::known(dao_notes_pub_x)),
103            Witness::Base(Value::known(dao_notes_pub_y)),
104            Witness::Base(Value::known(dao_proposer_pub_x)),
105            Witness::Base(Value::known(dao_proposer_pub_y)),
106            Witness::Base(Value::known(dao_proposals_pub_x)),
107            Witness::Base(Value::known(dao_proposals_pub_y)),
108            Witness::Base(Value::known(dao_votes_pub_x)),
109            Witness::Base(Value::known(dao_votes_pub_y)),
110            Witness::Base(Value::known(dao_exec_secret_key.inner())),
111        ];
112        // Early exec key
113        match dao_early_exec_secret_key {
114            Some(dao_early_exec_secret_key) => prover_witnesses
115                .push(Witness::Base(Value::known(dao_early_exec_secret_key.inner()))),
116            None => {
117                let (dao_early_exec_pub_x, dao_early_exec_pub_y) =
118                    self.dao.early_exec_public_key.xy();
119                prover_witnesses.push(Witness::Base(Value::known(dao_early_exec_pub_x)));
120                prover_witnesses.push(Witness::Base(Value::known(dao_early_exec_pub_y)));
121            }
122        };
123        // Rest witnesses
124        prover_witnesses.extend_from_slice(&[
125            Witness::Base(Value::known(self.dao.bulla_blind.inner())),
126            // Votes
127            Witness::Base(Value::known(pallas::Base::from(self.yes_vote_value))),
128            Witness::Base(Value::known(pallas::Base::from(self.all_vote_value))),
129            Witness::Scalar(Value::known(self.yes_vote_blind.inner())),
130            Witness::Scalar(Value::known(self.all_vote_blind.inner())),
131            // Time checks
132            Witness::Base(Value::known(current_blockwindow)),
133            // Signature secret
134            Witness::Base(Value::known(self.signature_secret.inner())),
135        ]);
136
137        debug!(target: "contract::dao::client::exec", "proposal_bulla: {:?}", proposal_bulla);
138        let public_inputs = vec![
139            proposal_bulla.inner(),
140            proposal_auth_calls_commit,
141            current_blockwindow,
142            *yes_vote_commit_coords.x(),
143            *yes_vote_commit_coords.y(),
144            *all_vote_commit_coords.x(),
145            *all_vote_commit_coords.y(),
146            signature_public.x(),
147            signature_public.y(),
148        ];
149        //darkfi::zk::export_witness_json("proof/witness/exec.json", &prover_witnesses, &public_inputs);
150
151        let circuit = ZkCircuit::new(prover_witnesses, exec_zkbin);
152        let input_proof = Proof::create(exec_pk, &[circuit], &public_inputs, &mut OsRng)?;
153        proofs.push(input_proof);
154
155        let params = DaoExecParams {
156            proposal_bulla,
157            proposal_auth_calls: self.proposal.auth_calls,
158            blind_total_vote: DaoBlindAggregateVote { yes_vote_commit, all_vote_commit },
159            early_exec: dao_early_exec_secret_key.is_some(),
160            signature_public,
161        };
162
163        Ok((params, proofs))
164    }
165}