darkfi_dao_contract/client/
auth_xfer.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_money_contract::model::CoinAttributes;
20use darkfi_sdk::{
21    crypto::{note::ElGamalEncryptedNote, poseidon_hash, BaseBlind, PublicKey, SecretKey},
22    pasta::pallas,
23};
24
25use rand::rngs::OsRng;
26
27use darkfi::{
28    zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
29    zkas::ZkBinary,
30    Result,
31};
32
33use crate::model::{Dao, DaoAuthMoneyTransferParams, DaoProposal, VecAuthCallCommit};
34
35pub struct DaoAuthMoneyTransferCall {
36    pub proposal: DaoProposal,
37    pub proposal_coinattrs: Vec<CoinAttributes>,
38    pub dao: Dao,
39    pub input_user_data_blind: BaseBlind,
40    pub dao_coin_attrs: CoinAttributes,
41}
42
43impl DaoAuthMoneyTransferCall {
44    pub fn make(
45        self,
46        auth_xfer_zkbin: &ZkBinary,
47        auth_xfer_pk: &ProvingKey,
48        auth_xfer_enc_coin_zkbin: &ZkBinary,
49        auth_xfer_enc_coin_pk: &ProvingKey,
50    ) -> Result<(DaoAuthMoneyTransferParams, Vec<Proof>)> {
51        let mut proofs = vec![];
52
53        // Proof for each coin of verifiable encryption
54
55        let mut enc_attrs = vec![];
56        let mut proposal_coinattrs = self.proposal_coinattrs;
57        proposal_coinattrs.push(self.dao_coin_attrs.clone());
58        for coin_attrs in proposal_coinattrs {
59            let coin = coin_attrs.to_coin();
60
61            let ephem_secret = SecretKey::random(&mut OsRng);
62            let ephem_pubkey = PublicKey::from_secret(ephem_secret);
63            let (ephem_x, ephem_y) = ephem_pubkey.xy();
64
65            let value_base = pallas::Base::from(coin_attrs.value);
66
67            let note = [
68                value_base,
69                coin_attrs.token_id.inner(),
70                coin_attrs.spend_hook.inner(),
71                coin_attrs.user_data,
72                coin_attrs.blind.inner(),
73            ];
74            let enc_note =
75                ElGamalEncryptedNote::encrypt_unsafe(note, &ephem_secret, &coin_attrs.public_key)?;
76
77            let prover_witnesses = vec![
78                Witness::EcNiPoint(Value::known(coin_attrs.public_key.inner())),
79                Witness::Base(Value::known(value_base)),
80                Witness::Base(Value::known(coin_attrs.token_id.inner())),
81                Witness::Base(Value::known(coin_attrs.spend_hook.inner())),
82                Witness::Base(Value::known(coin_attrs.user_data)),
83                Witness::Base(Value::known(coin_attrs.blind.inner())),
84                Witness::Base(Value::known(ephem_secret.inner())),
85            ];
86
87            let public_inputs = vec![
88                coin.inner(),
89                ephem_x,
90                ephem_y,
91                enc_note.encrypted_values[0],
92                enc_note.encrypted_values[1],
93                enc_note.encrypted_values[2],
94                enc_note.encrypted_values[3],
95                enc_note.encrypted_values[4],
96            ];
97
98            //darkfi::zk::export_witness_json("proof/witness/auth-money-transfer-enc-coin.json", &prover_witnesses, &public_inputs);
99            let circuit = ZkCircuit::new(prover_witnesses, auth_xfer_enc_coin_zkbin);
100            let proof =
101                Proof::create(auth_xfer_enc_coin_pk, &[circuit], &public_inputs, &mut OsRng)?;
102            proofs.push(proof);
103
104            enc_attrs.push(enc_note);
105        }
106
107        // Build the main proof
108
109        let ephem_secret = SecretKey::random(&mut OsRng);
110        let change_ephem_pubkey = PublicKey::from_secret(ephem_secret);
111        let (ephem_x, ephem_y) = change_ephem_pubkey.xy();
112
113        let dao_change_value = pallas::Base::from(self.dao_coin_attrs.value);
114
115        let note = [
116            dao_change_value,
117            self.dao_coin_attrs.token_id.inner(),
118            self.dao_coin_attrs.blind.inner(),
119        ];
120
121        let dao_change_attrs =
122            ElGamalEncryptedNote::encrypt_unsafe(note, &ephem_secret, &self.dao.notes_public_key)?;
123
124        let params = DaoAuthMoneyTransferParams { enc_attrs, dao_change_attrs };
125
126        let dao_proposer_limit = pallas::Base::from(self.dao.proposer_limit);
127        let dao_quorum = pallas::Base::from(self.dao.quorum);
128        let dao_early_exec_quorum = pallas::Base::from(self.dao.early_exec_quorum);
129        let dao_approval_ratio_quot = pallas::Base::from(self.dao.approval_ratio_quot);
130        let dao_approval_ratio_base = pallas::Base::from(self.dao.approval_ratio_base);
131        let dao_notes_public_key = self.dao.notes_public_key.inner();
132        let (dao_proposer_pub_x, dao_proposer_pub_y) = self.dao.proposer_public_key.xy();
133        let (dao_proposals_pub_x, dao_proposals_pub_y) = self.dao.proposals_public_key.xy();
134        let (dao_votes_pub_x, dao_votes_pub_y) = self.dao.votes_public_key.xy();
135        let (dao_exec_pub_x, dao_exec_pub_y) = self.dao.exec_public_key.xy();
136        let (dao_early_exec_pub_x, dao_early_exec_pub_y) = self.dao.early_exec_public_key.xy();
137
138        let input_user_data_enc =
139            poseidon_hash([self.dao.to_bulla().inner(), self.input_user_data_blind.inner()]);
140
141        let prover_witnesses = vec![
142            // proposal params
143            Witness::Base(Value::known(self.proposal.auth_calls.commit())),
144            Witness::Base(Value::known(pallas::Base::from(self.proposal.creation_blockwindow))),
145            Witness::Base(Value::known(pallas::Base::from(self.proposal.duration_blockwindows))),
146            Witness::Base(Value::known(self.proposal.user_data)),
147            Witness::Base(Value::known(self.proposal.blind.inner())),
148            // DAO params
149            Witness::Base(Value::known(dao_proposer_limit)),
150            Witness::Base(Value::known(dao_quorum)),
151            Witness::Base(Value::known(dao_early_exec_quorum)),
152            Witness::Base(Value::known(dao_approval_ratio_quot)),
153            Witness::Base(Value::known(dao_approval_ratio_base)),
154            Witness::Base(Value::known(self.dao.gov_token_id.inner())),
155            Witness::EcNiPoint(Value::known(dao_notes_public_key)),
156            Witness::Base(Value::known(dao_proposer_pub_x)),
157            Witness::Base(Value::known(dao_proposer_pub_y)),
158            Witness::Base(Value::known(dao_proposals_pub_x)),
159            Witness::Base(Value::known(dao_proposals_pub_y)),
160            Witness::Base(Value::known(dao_votes_pub_x)),
161            Witness::Base(Value::known(dao_votes_pub_y)),
162            Witness::Base(Value::known(dao_exec_pub_x)),
163            Witness::Base(Value::known(dao_exec_pub_y)),
164            Witness::Base(Value::known(dao_early_exec_pub_x)),
165            Witness::Base(Value::known(dao_early_exec_pub_y)),
166            Witness::Base(Value::known(self.dao.bulla_blind.inner())),
167            // Dao input user data blind
168            Witness::Base(Value::known(self.input_user_data_blind.inner())),
169            // Dao output coin attrs
170            Witness::Base(Value::known(dao_change_value)),
171            Witness::Base(Value::known(self.dao_coin_attrs.token_id.inner())),
172            Witness::Base(Value::known(self.dao_coin_attrs.blind.inner())),
173            // DAO::exec() func ID
174            Witness::Base(Value::known(self.dao_coin_attrs.spend_hook.inner())),
175            // Encrypted change DAO output
176            Witness::Base(Value::known(ephem_secret.inner())),
177        ];
178
179        let public_inputs = vec![
180            self.proposal.to_bulla().inner(),
181            input_user_data_enc,
182            self.dao_coin_attrs.to_coin().inner(),
183            self.dao_coin_attrs.spend_hook.inner(),
184            self.proposal.auth_calls.commit(),
185            ephem_x,
186            ephem_y,
187            dao_change_attrs.encrypted_values[0],
188            dao_change_attrs.encrypted_values[1],
189            dao_change_attrs.encrypted_values[2],
190        ];
191
192        //darkfi::zk::export_witness_json("proof/witness/auth-money-transfer.json", &prover_witnesses, &public_inputs);
193        let circuit = ZkCircuit::new(prover_witnesses, auth_xfer_zkbin);
194        let proof = Proof::create(auth_xfer_pk, &[circuit], &public_inputs, &mut OsRng)?;
195        proofs.push(proof);
196
197        Ok((params, proofs))
198    }
199}