contract/dao/client: use diff signatures per input in propose and vote

This commit is contained in:
skoupidi
2025-12-16 16:48:38 +02:00
parent 34cafd044b
commit d5be2cf726
6 changed files with 61 additions and 69 deletions

View File

@@ -58,6 +58,7 @@ use darkfi_money_contract::{
use darkfi_sdk::{
bridgetree,
crypto::{
keypair::{Address, StandardAddress},
pasta_prelude::PrimeField,
poseidon_hash,
smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
@@ -1587,8 +1588,6 @@ impl Drk {
/// Import given DAO vote into the wallet.
pub async fn put_dao_vote(&self, vote: &VoteRecord) -> WalletDbResult<()> {
println!("Importing DAO vote into wallet");
// Create an SQL `INSERT OR REPLACE` query
let query = format!(
"INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9);",
@@ -1620,8 +1619,6 @@ impl Drk {
// Execute the query
self.wallet.exec_sql(&query, params)?;
println!("DAO vote added to wallet");
Ok(())
}
@@ -1872,6 +1869,9 @@ impl Drk {
if let Some(name) = name {
let dao = self.get_dao_by_name(name).await?;
output.push(format!("{dao}"));
let address: Address =
StandardAddress::from_public(self.network, dao.params.dao.notes_public_key).into();
output.push(format!("Wallet Address: {address}"));
return Ok(());
}
@@ -2521,10 +2521,8 @@ impl Drk {
inputs.push(input);
}
// Now create the parameters for the proposal tx
let signature_secret = SecretKey::random(&mut OsRng);
// Fetch the daos Merkle tree to compute the DAO Merkle path and root
// Now create the parameters for the proposal tx.
// Fetch the daos Merkle tree to compute the DAO Merkle path and root.
let (daos_tree, _) = self.get_dao_trees().await?;
let (dao_merkle_path, dao_merkle_root) = {
let root = daos_tree.root(0).unwrap();
@@ -2546,10 +2544,9 @@ impl Drk {
dao_leaf_position: dao.leaf_position.unwrap(),
dao_merkle_path,
dao_merkle_root,
signature_secret,
};
let (params, proofs) = call.make(
let (params, proofs, signature_secrets) = call.make(
&dao.params.proposer_secret_key.unwrap(),
&propose_burn_zkbin,
&propose_burn_pk,
@@ -2568,8 +2565,8 @@ impl Drk {
// We first have to execute the fee-less tx to gather its used gas, and then we feed
// it into the fee-creating function.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let tree = self.get_money_tree().await?;
let (fee_call, fee_proofs, fee_secrets) =
@@ -2580,7 +2577,7 @@ impl Drk {
// Now build the actual transaction and sign it with all necessary keys.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let sigs = tx.create_sigs(&fee_secrets)?;
tx.signatures.push(sigs);
@@ -2704,10 +2701,8 @@ impl Drk {
inputs.push(input);
}
// Now create the parameters for the proposal tx
let signature_secret = SecretKey::random(&mut OsRng);
// Fetch the daos Merkle tree to compute the DAO Merkle path and root
// Now create the parameters for the proposal tx.
// Fetch the daos Merkle tree to compute the DAO Merkle path and root.
let (daos_tree, _) = self.get_dao_trees().await?;
let (dao_merkle_path, dao_merkle_root) = {
let root = daos_tree.root(0).unwrap();
@@ -2729,10 +2724,9 @@ impl Drk {
dao_leaf_position: dao.leaf_position.unwrap(),
dao_merkle_path,
dao_merkle_root,
signature_secret,
};
let (params, proofs) = call.make(
let (params, proofs, signature_secrets) = call.make(
&dao.params.proposer_secret_key.unwrap(),
&propose_burn_zkbin,
&propose_burn_pk,
@@ -2751,8 +2745,8 @@ impl Drk {
// We first have to execute the fee-less tx to gather its used gas, and then we feed
// it into the fee-creating function.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let tree = self.get_money_tree().await?;
let (fee_call, fee_proofs, fee_secrets) =
@@ -2763,7 +2757,7 @@ impl Drk {
// Now build the actual transaction and sign it with all necessary keys.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let sigs = tx.create_sigs(&fee_secrets)?;
tx.signatures.push(sigs);
@@ -2891,7 +2885,6 @@ impl Drk {
let dao_vote_main_pk = ProvingKey::build(dao_vote_main_zkbin.k, &dao_vote_main_circuit);
// Now create the parameters for the vote tx
let signature_secret = SecretKey::random(&mut OsRng);
let mut inputs = Vec::with_capacity(gov_owncoins_to_use.len());
for gov_owncoin in gov_owncoins_to_use {
// Skip governance coins that are not part of the snapshot
@@ -2915,7 +2908,6 @@ impl Drk {
note: gov_owncoin.note.clone(),
leaf_position: gov_owncoin.leaf_position,
merkle_path,
signature_secret,
};
inputs.push(input);
}
@@ -2946,7 +2938,7 @@ impl Drk {
current_blockwindow,
};
let (params, proofs) = call.make(
let (params, proofs, signature_secrets) = call.make(
&dao_vote_burn_zkbin,
&dao_vote_burn_pk,
&dao_vote_main_zkbin,
@@ -2964,8 +2956,8 @@ impl Drk {
// We first have to execute the fee-less tx to gather its used gas, and then we feed
// it into the fee-creating function.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let tree = self.get_money_tree().await?;
let (fee_call, fee_proofs, fee_secrets) =
@@ -2976,7 +2968,7 @@ impl Drk {
// Now build the actual transaction and sign it with all necessary keys.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let sigs = tx.create_sigs(&fee_secrets)?;
tx.signatures.push(sigs);

View File

@@ -135,6 +135,7 @@ Bulla blind: 6TVkmM...Jjd5zC
Leaf position: None
Transaction hash: None
Call index: None
Wallet Address: DX7N6v...5Lz8Pp
```
## Minting
@@ -186,13 +187,14 @@ Leaf position: Position(0)
Mint height: 10
Transaction hash: 2e7931f200c1485ea7752076e199708b011a504d71e69d60ed606817c5ff4bd5
Call index: 0
Wallet Address: DX7N6v...5Lz8Pp
```
## Sending money to the treasury
Let's send some tokens to the DAO's treasury so we're able to make
a proposal to send those somewhere. First, find the DAO bulla, the
dao contract spend hook and the DAO notes public key.
a proposal to send those somewhere. First, find the DAO wallet address,
the DAO bulla and the dao contract spend hook.
Then create a transfer transaction as follows:
@@ -231,10 +233,11 @@ Leaf position: Position(0)
Mint height: 10
Transaction hash: 2e7931f200c1485ea7752076e199708b011a504d71e69d60ed606817c5ff4bd5
Call index: 0
Wallet Address: DX7N6v...5Lz8Pp
```
```shell
drk> transfer 10 DAWN {DAO_NOTES_PUBLIC_KEY} {DAO_CONTRACT_SPEND_HOOK} {DAO_BULLA} | broadcast
drk> transfer 10 DAWN {DAO_WALLET_ADDRESS} {DAO_CONTRACT_SPEND_HOOK} {DAO_BULLA} | broadcast
[mark_tx_spend] Processing transaction: a4db439f75de88457cadd849131394ae37723c943ea5c088b218d6dc0f7982f1
[mark_tx_spend] Found Money contract in call 0
@@ -565,7 +568,7 @@ instead of transfering some to the DAO, we will mint them
directly into it:
```shell
drk> token mint ANON 20 {DAO_NOTES_PUBLIC_KEY} {DAO_CONTRACT_SPEND_HOOK} {DAO_BULLA} | broadcast
drk> token mint ANON 20 {DAO_WALLET_ADDRESS} {DAO_CONTRACT_SPEND_HOOK} {DAO_BULLA} | broadcast
[mark_tx_spend] Processing transaction: 781632eb1d0e4566582c1bb34f4a99516d62357761659d4e5e965ac9d199b581
[mark_tx_spend] Found Money contract in call 0
@@ -638,10 +641,11 @@ Leaf position: Position(3)
Mint height: 23
Transaction hash: cfc31bee7d198d7d59e9f40f76a98e93230320ec6dd8c606af32d9bee28fcf0e
Call index: 0
Wallet Address: Ui5C2e...1IO4Aa
```
```shell
drk> dao propose-transfer AnonDAO 1 6.9 ANON {DAWN_DAO_NOTES_PUBLIC_KEY} {DAO_CONTRACT_SPEND_HOOK} {DAWN_DAO_BULLA}
drk> dao propose-transfer AnonDAO 1 6.9 ANON {DAWN_DAO_WALLET_ADDRESS} {DAO_CONTRACT_SPEND_HOOK} {DAWN_DAO_BULLA}
Generated proposal: {PROPOSAL_BULLA}
```

View File

@@ -59,7 +59,6 @@ pub struct DaoProposeCall<'a, T: StorageAdapter<Value = pallas::Base>> {
pub dao_leaf_position: bridgetree::Position,
pub dao_merkle_path: Vec<MerkleNode>,
pub dao_merkle_root: MerkleNode,
pub signature_secret: SecretKey,
}
impl<T: StorageAdapter<Value = pallas::Base>> DaoProposeCall<'_, T> {
@@ -70,14 +69,12 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoProposeCall<'_, T> {
burn_pk: &ProvingKey,
main_zkbin: &ZkBinary,
main_pk: &ProvingKey,
) -> Result<(DaoProposeParams, Vec<Proof>)> {
) -> Result<(DaoProposeParams, Vec<Proof>, Vec<SecretKey>)> {
let mut proofs = vec![];
let mut signature_secrets = vec![];
let gov_token_blind = Blind::random(&mut OsRng);
let smt_null_root = self.money_null_smt.root();
let signature_public = PublicKey::from_secret(self.signature_secret);
let (sig_x, sig_y) = signature_public.xy();
let mut inputs = vec![];
let mut total_funds = 0;
@@ -111,6 +108,10 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoProposeCall<'_, T> {
)
}
let signature_secret = SecretKey::random(&mut OsRng);
let signature_public = PublicKey::from_secret(signature_secret);
let (sig_x, sig_y) = signature_public.xy();
let prover_witnesses = vec![
Witness::Base(Value::known(input.secret.inner())),
Witness::Base(Value::known(pallas::Base::from(note.value))),
@@ -123,7 +124,7 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoProposeCall<'_, T> {
Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
Witness::SparseMerklePath(Value::known(smt_null_path.path)),
Witness::Base(Value::known(self.signature_secret.inner())),
Witness::Base(Value::known(signature_secret.inner())),
];
// TODO: We need a generic ZkSet widget to avoid doing this all the time
@@ -165,6 +166,7 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoProposeCall<'_, T> {
let proving_key = &burn_pk;
let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(input_proof);
signature_secrets.push(signature_secret);
let input = DaoProposeParamsInput {
value_commit,
@@ -258,6 +260,6 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoProposeCall<'_, T> {
inputs,
};
Ok((params, proofs))
Ok((params, proofs, signature_secrets))
}
}

View File

@@ -49,7 +49,6 @@ pub struct DaoVoteInput {
pub note: darkfi_money_contract::client::MoneyNote,
pub leaf_position: bridgetree::Position,
pub merkle_path: Vec<MerkleNode>,
pub signature_secret: SecretKey,
}
// Inside ZK proof, check proposal is correct.
@@ -70,7 +69,7 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
burn_pk: &ProvingKey,
main_zkbin: &ZkBinary,
main_pk: &ProvingKey,
) -> Result<(DaoVoteParams, Vec<Proof>)> {
) -> Result<(DaoVoteParams, Vec<Proof>, Vec<SecretKey>)> {
debug!(target: "contract::dao::client::vote", "make()");
if self.dao.to_bulla() != self.proposal.dao_bulla {
@@ -79,6 +78,7 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
let proposal_bulla = self.proposal.to_bulla();
let mut proofs = vec![];
let mut signature_secrets = vec![];
let gov_token_blind = pallas::Base::random(&mut OsRng);
@@ -113,7 +113,8 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
all_vote_value += input.note.value;
all_vote_blind += value_blind;
let signature_public = PublicKey::from_secret(input.signature_secret);
let signature_secret = SecretKey::random(&mut OsRng);
let signature_public = PublicKey::from_secret(signature_secret);
// Note from the previous output
let note = input.note;
@@ -152,7 +153,7 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
Witness::SparseMerklePath(Value::known(smt_null_path.path)),
Witness::Base(Value::known(input.signature_secret.inner())),
Witness::Base(Value::known(signature_secret.inner())),
];
let merkle_root = {
@@ -199,6 +200,7 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
debug!(target: "contract::dao::client::vote", "input_proof Proof::create()");
let input_proof = Proof::create(burn_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(input_proof);
signature_secrets.push(signature_secret);
let input = DaoVoteParamsInput {
vote_commit,
@@ -327,6 +329,6 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
let params =
DaoVoteParams { token_commit, proposal_bulla, yes_vote_commit, note: enc_note, inputs };
Ok((params, proofs))
Ok((params, proofs, signature_secrets))
}
}

View File

@@ -130,7 +130,6 @@ impl TestHarness {
blind: Blind::random(&mut OsRng),
};
let signature_secret = SecretKey::random(&mut OsRng);
let dao_bulla = dao.to_bulla();
let call = DaoProposeCall {
@@ -144,10 +143,9 @@ impl TestHarness {
.witness(*wallet.dao_leafs.get(&dao_bulla).unwrap(), 0)
.unwrap(),
dao_merkle_root: wallet.dao_merkle_tree.root(0).unwrap(),
signature_secret,
};
let (params, proofs) = call.make(
let (params, proofs, signature_secrets) = call.make(
dao_proposer_secret_key,
dao_propose_burn_zkbin,
dao_propose_burn_pk,
@@ -166,8 +164,8 @@ impl TestHarness {
let mut fee_signature_secrets = None;
if self.verify_fees {
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
self.append_fee_call(proposer, tx, block_height, &[]).await?;
@@ -180,8 +178,8 @@ impl TestHarness {
// Now build the actual transaction and sign it with necessary keys.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
if let Some(fee_signature_secrets) = fee_signature_secrets {
let sigs = tx.create_sigs(&fee_signature_secrets)?;
tx.signatures.push(sigs);
@@ -250,7 +248,6 @@ impl TestHarness {
blind: Blind::random(&mut OsRng),
};
let signature_secret = SecretKey::random(&mut OsRng);
let dao_bulla = dao.to_bulla();
let call = DaoProposeCall {
@@ -264,10 +261,9 @@ impl TestHarness {
.witness(*wallet.dao_leafs.get(&dao_bulla).unwrap(), 0)
.unwrap(),
dao_merkle_root: wallet.dao_merkle_tree.root(0).unwrap(),
signature_secret,
};
let (params, proofs) = call.make(
let (params, proofs, signature_secrets) = call.make(
dao_proposer_secret_key,
dao_propose_burn_zkbin,
dao_propose_burn_pk,
@@ -286,8 +282,8 @@ impl TestHarness {
let mut fee_signature_secrets = None;
if self.verify_fees {
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
self.append_fee_call(proposer, tx, block_height, &[]).await?;
@@ -300,8 +296,8 @@ impl TestHarness {
// Now build the actual transaction and sign it with necessary keys.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
if let Some(fee_signature_secrets) = fee_signature_secrets {
let sigs = tx.create_sigs(&fee_signature_secrets)?;
tx.signatures.push(sigs);

View File

@@ -31,11 +31,10 @@ use darkfi_money_contract::{
model::MoneyFeeParamsV1,
};
use darkfi_sdk::{
crypto::{contract_id::DAO_CONTRACT_ID, MerkleNode, SecretKey},
crypto::{contract_id::DAO_CONTRACT_ID, MerkleNode},
ContractCall,
};
use darkfi_serial::AsyncEncodable;
use rand::rngs::OsRng;
use tracing::debug;
use super::{Holder, TestHarness};
@@ -68,14 +67,11 @@ impl TestHarness {
.unwrap()
.clone();
let signature_secret = SecretKey::random(&mut OsRng);
let input = DaoVoteInput {
secret: wallet.keypair.secret,
note: vote_owncoin.note.clone(),
leaf_position: vote_owncoin.leaf_position,
merkle_path: snapshot_money_merkle_tree.witness(vote_owncoin.leaf_position, 0).unwrap(),
signature_secret,
};
let block_target = wallet.validator.consensus.module.read().await.target;
@@ -89,7 +85,7 @@ impl TestHarness {
current_blockwindow,
};
let (params, proofs) = call.make(
let (params, proofs, signature_secrets) = call.make(
dao_vote_burn_zkbin,
dao_vote_burn_pk,
dao_vote_main_zkbin,
@@ -107,8 +103,8 @@ impl TestHarness {
let mut fee_signature_secrets = None;
if self.verify_fees {
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
self.append_fee_call(voter, tx, block_height, &[]).await?;
@@ -121,8 +117,8 @@ impl TestHarness {
// Now build the actual transaction and sign it with necessary keys.
let mut tx = tx_builder.build()?;
let sigs = tx.create_sigs(&[signature_secret])?;
tx.signatures = vec![sigs];
let sigs = tx.create_sigs(&signature_secrets)?;
tx.signatures.push(sigs);
if let Some(fee_signature_secrets) = fee_signature_secrets {
let sigs = tx.create_sigs(&fee_signature_secrets)?;
tx.signatures.push(sigs);