dao: cleanup by introducing the CoinParams -> Coin, Dao -> DaoBulla, and Proposal -> ProposalBulla paradigm. use consistently throughout the builders.

This commit is contained in:
x
2023-12-18 12:22:13 +00:00
parent 7529e4a655
commit 39499ac2d7
12 changed files with 162 additions and 164 deletions

View File

@@ -64,10 +64,6 @@ circuit "DaoProposeMain" {
);
constrain_instance(proposal_bulla);
# Rangeproof check for proposal amount
zero = witness_base(0);
less_than_strict(zero, proposal_amount);
# This is the main check
# We check that dao_proposer_limit <= total_funds
one = witness_base(1);

View File

@@ -20,6 +20,7 @@ use darkfi_sdk::{
crypto::{pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, SecretKey},
pasta::pallas,
};
use darkfi_money_contract::model::CoinParams;
use halo2_proofs::circuit::Value;
use log::debug;
@@ -31,12 +32,11 @@ use darkfi::{
Result,
};
use super::{DaoInfo, DaoProposalInfo};
use crate::model::{DaoBlindAggregateVote, DaoExecParams, DaoProposalBulla};
use crate::model::{Dao, DaoProposal, DaoBlindAggregateVote, DaoExecParams, DaoProposalBulla};
pub struct DaoExecCall {
pub proposal: DaoProposalInfo,
pub dao: DaoInfo,
pub proposal: DaoProposal,
pub dao: Dao,
pub yes_vote_value: u64,
pub all_vote_value: u64,
pub yes_vote_blind: pallas::Scalar,
@@ -73,63 +73,31 @@ impl DaoExecCall {
let user_spend_hook = pallas::Base::from(0);
let user_data = pallas::Base::from(0);
let input_value = pallas::Base::from(self.input_value);
let change = input_value - proposal_amount;
let change = self.input_value - self.proposal.amount;
let dao_bulla = poseidon_hash::<8>([
dao_proposer_limit,
dao_quorum,
dao_approval_ratio_quot,
dao_approval_ratio_base,
self.dao.gov_token_id.inner(),
dao_pub_x,
dao_pub_y,
self.dao.bulla_blind,
]);
let dao_bulla = self.dao.to_bulla();
assert_eq!(dao_bulla, self.proposal.dao_bulla);
let proposal_bulla = self.proposal.to_bulla();
let proposal_bulla = DaoProposalBulla::from(poseidon_hash::<6>([
proposal_dest_x,
proposal_dest_y,
proposal_amount,
self.proposal.token_id.inner(),
dao_bulla,
self.proposal.blind,
]));
let coin_0 = CoinParams {
public_key: self.proposal.dest,
value: self.proposal.amount,
token_id: self.proposal.token_id,
serial: self.user_serial,
spend_hook: user_spend_hook,
user_data: user_data,
}.to_coin();
debug!("created coin_0 {:?}", coin_0);
let coin_0 = poseidon_hash::<7>([
proposal_dest_x,
proposal_dest_y,
proposal_amount,
self.proposal.token_id.inner(),
self.user_serial,
user_spend_hook,
user_data,
]);
debug!("created coin {:?}", coin_0);
debug!(" proposal_dest_x: {:?}", proposal_dest_x);
debug!(" proposal_dest_y: {:?}", proposal_dest_y);
debug!(" proposal_amount: {:?}", proposal_amount);
debug!(" proposal.token_id: {:?}", self.proposal.token_id.inner());
debug!(" user_serial: {:?}", self.user_serial);
debug!(" user_spend_hook: {:?}", user_spend_hook);
debug!(" user_data: {:?}", user_data);
let coin_1 = poseidon_hash::<7>([
dao_pub_x,
dao_pub_y,
change,
self.proposal.token_id.inner(),
self.dao_serial,
self.hook_dao_exec,
dao_bulla,
]);
debug!("created coin {:?}", coin_1);
debug!(" dao_pub_x: {:?}", dao_pub_x);
debug!(" dao_pub_y: {:?}", dao_pub_y);
debug!(" change: {:?}", change);
debug!(" proposal.token_id: {:?}", self.proposal.token_id.inner());
debug!(" dao_serial: {:?}", self.dao_serial);
debug!(" hook_dao_exec: {:?}", self.hook_dao_exec);
debug!(" dao_bulla: {:?}", dao_bulla);
let coin_1 = CoinParams {
public_key: self.dao.public_key,
value: change,
token_id: self.proposal.token_id,
serial: self.dao_serial,
spend_hook: self.hook_dao_exec,
user_data: dao_bulla.inner(),
}.to_coin();
debug!("created coin_1 {:?}", coin_1);
let yes_vote_commit = pedersen_commitment_u64(self.yes_vote_value, self.yes_vote_blind);
let yes_vote_commit_coords = yes_vote_commit.to_affine().coordinates().unwrap();
@@ -174,14 +142,14 @@ impl DaoExecCall {
Witness::Base(Value::known(self.input_user_data_blind)),
];
let input_user_data_enc = poseidon_hash([dao_bulla, self.input_user_data_blind]);
let input_user_data_enc = poseidon_hash([dao_bulla.inner(), self.input_user_data_blind]);
debug!(target: "dao", "input_user_data_enc: {:?}", input_user_data_enc);
debug!(target: "dao", "proposal_bulla: {:?}", proposal_bulla);
let public_inputs = vec![
proposal_bulla.inner(),
coin_0,
coin_1,
coin_0.inner(),
coin_1.inner(),
*yes_vote_commit_coords.x(),
*yes_vote_commit_coords.y(),
*all_vote_commit_coords.x(),

View File

@@ -22,13 +22,13 @@ use darkfi::{
Result,
};
use darkfi_sdk::{
crypto::{poseidon_hash, PublicKey, SecretKey, TokenId},
crypto::{PublicKey, SecretKey, TokenId},
pasta::pallas,
};
use log::debug;
use rand::rngs::OsRng;
use crate::model::DaoMintParams;
use crate::model::{Dao, DaoMintParams};
#[derive(Clone)]
pub struct DaoInfo {
@@ -42,7 +42,7 @@ pub struct DaoInfo {
}
pub fn make_mint_call(
dao: &DaoInfo,
dao: &Dao,
dao_secret_key: &SecretKey,
dao_mint_zkbin: &ZkBinary,
dao_mint_pk: &ProvingKey,
@@ -54,19 +54,6 @@ pub fn make_mint_call(
let dao_approval_ratio_quot = pallas::Base::from(dao.approval_ratio_quot);
let dao_approval_ratio_base = pallas::Base::from(dao.approval_ratio_base);
let (pub_x, pub_y) = dao.public_key.xy();
let dao_bulla = poseidon_hash([
dao_proposer_limit,
dao_quorum,
dao_approval_ratio_quot,
dao_approval_ratio_base,
dao.gov_token_id.inner(),
pub_x,
pub_y,
dao.bulla_blind,
]);
// NOTE: It's important to keep these in the same order as the zkas code.
let prover_witnesses = vec![
Witness::Base(halo2::Value::known(dao_proposer_limit)),
@@ -78,7 +65,9 @@ pub fn make_mint_call(
Witness::Base(halo2::Value::known(dao.bulla_blind)),
];
let public = vec![pub_x, pub_y, dao_bulla];
let (pub_x, pub_y) = dao.public_key.xy();
let dao_bulla = dao.to_bulla();
let public = vec![pub_x, pub_y, dao_bulla.inner()];
let circuit = ZkCircuit::new(prover_witnesses, dao_mint_zkbin);
let proof = Proof::create(dao_mint_pk, &[circuit], &public, &mut OsRng)?;

View File

@@ -26,7 +26,7 @@ pub use mint::{make_mint_call, DaoInfo};
/// * `DaoProposeCall` is what creates the call data used on chain.
/// * `DaoProposeNote` is the secret shared info transmitted between DAO members.
pub mod propose;
pub use propose::{DaoProposalInfo, DaoProposeCall, DaoProposeNote, DaoProposeStakeInput};
pub use propose::{DaoProposeCall, DaoProposeNote, DaoProposeStakeInput};
/// Provides core structs for DAO::vote()
///

View File

@@ -21,11 +21,12 @@ use darkfi_sdk::{
bridgetree::Hashable,
crypto::{
note::AeadEncryptedNote, pasta_prelude::*, pedersen::pedersen_commitment_u64,
poseidon_hash, MerkleNode, PublicKey, SecretKey, TokenId,
poseidon_hash, MerkleNode, PublicKey, SecretKey,
},
pasta::pallas,
};
use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
use darkfi_money_contract::model::CoinParams;
use rand::rngs::OsRng;
use darkfi::{
@@ -34,21 +35,11 @@ use darkfi::{
Result,
};
use crate::model::{DaoProposalBulla, DaoProposeParams, DaoProposeParamsInput};
use super::DaoInfo;
#[derive(SerialEncodable, SerialDecodable, Clone)]
pub struct DaoProposalInfo {
pub dest: PublicKey,
pub amount: u64,
pub token_id: TokenId,
pub blind: pallas::Base,
}
use crate::model::{Dao, DaoProposal, DaoProposeParams, DaoProposeParamsInput};
#[derive(SerialEncodable, SerialDecodable)]
pub struct DaoProposeNote {
pub proposal: DaoProposalInfo,
pub proposal: DaoProposal,
}
pub struct DaoProposeStakeInput {
@@ -61,8 +52,8 @@ pub struct DaoProposeStakeInput {
pub struct DaoProposeCall {
pub inputs: Vec<DaoProposeStakeInput>,
pub proposal: DaoProposalInfo,
pub dao: DaoInfo,
pub proposal: DaoProposal,
pub dao: Dao,
pub dao_leaf_position: bridgetree::Position,
pub dao_merkle_path: Vec<MerkleNode>,
pub dao_merkle_root: MerkleNode,
@@ -112,21 +103,20 @@ impl DaoProposeCall {
];
let public_key = PublicKey::from_secret(input.secret);
let (pub_x, pub_y) = public_key.xy();
let coin = CoinParams {
public_key,
value: note.value,
token_id: note.token_id,
serial: note.serial,
spend_hook: pallas::Base::ZERO,
user_data: pallas::Base::ZERO,
}.to_coin();
let coin = poseidon_hash::<7>([
pub_x,
pub_y,
pallas::Base::from(note.value),
note.token_id.inner(),
note.serial,
pallas::Base::from(0),
pallas::Base::from(0),
]);
// TODO: We need a generic ZkSet widget to avoid doing this all the time
let merkle_root = {
let position: u64 = input.leaf_position.into();
let mut current = MerkleNode::from(coin);
let mut current = MerkleNode::from(coin.inner());
for (level, sibling) in input.merkle_path.iter().enumerate() {
let level = level as u8;
current = if position & (1 << level) == 0 {
@@ -179,30 +169,12 @@ impl DaoProposeCall {
let dao_quorum = pallas::Base::from(self.dao.quorum);
let dao_approval_ratio_quot = pallas::Base::from(self.dao.approval_ratio_quot);
let dao_approval_ratio_base = pallas::Base::from(self.dao.approval_ratio_base);
let (dao_pub_x, dao_pub_y) = self.dao.public_key.xy();
let dao_bulla = poseidon_hash::<8>([
dao_proposer_limit,
dao_quorum,
dao_approval_ratio_quot,
dao_approval_ratio_base,
self.dao.gov_token_id.inner(),
dao_pub_x,
dao_pub_y,
self.dao.bulla_blind,
]);
let dao_leaf_position: u64 = self.dao_leaf_position.into();
let proposal_bulla = DaoProposalBulla::from(poseidon_hash::<6>([
proposal_dest_x,
proposal_dest_y,
proposal_amount,
self.proposal.token_id.inner(),
dao_bulla,
self.proposal.blind,
]));
assert_eq!(self.dao.to_bulla(), self.proposal.dao_bulla);
let proposal_bulla = self.proposal.to_bulla();
let prover_witnesses = vec![
// Proposers total number of gov tokens

View File

@@ -26,6 +26,7 @@ use darkfi_sdk::{
pasta::pallas,
};
use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
use darkfi_money_contract::model::CoinParams;
use log::debug;
use rand::rngs::OsRng;
@@ -35,8 +36,7 @@ use darkfi::{
Result,
};
use super::{DaoInfo, DaoProposalInfo};
use crate::model::{DaoProposalBulla, DaoVoteParams, DaoVoteParamsInput};
use crate::model::{Dao, DaoProposal, DaoProposalBulla, DaoVoteParams, DaoVoteParamsInput};
#[derive(SerialEncodable, SerialDecodable)]
pub struct DaoVoteNote {
@@ -61,8 +61,8 @@ pub struct DaoVoteCall {
pub vote_option: bool,
pub yes_vote_blind: pallas::Scalar,
pub vote_keypair: Keypair,
pub proposal: DaoProposalInfo,
pub dao: DaoInfo,
pub proposal: DaoProposal,
pub dao: Dao,
}
impl DaoVoteCall {
@@ -111,21 +111,18 @@ impl DaoVoteCall {
];
let public_key = PublicKey::from_secret(input.secret);
let (pub_x, pub_y) = public_key.xy();
let coin = poseidon_hash::<7>([
pub_x,
pub_y,
pallas::Base::from(note.value),
note.token_id.inner(),
note.serial,
pallas::Base::from(0),
pallas::Base::from(0),
]);
let coin = CoinParams {
public_key,
value: note.value,
token_id: note.token_id,
serial: note.serial,
spend_hook: pallas::Base::ZERO,
user_data: pallas::Base::ZERO,
}.to_coin();
let merkle_root = {
let position: u64 = input.leaf_position.into();
let mut current = MerkleNode::from(coin);
let mut current = MerkleNode::from(coin.inner());
for (level, sibling) in input.merkle_path.iter().enumerate() {
let level = level as u8;
current = if position & (1 << level) == 0 {

View File

@@ -19,7 +19,7 @@
use core::str::FromStr;
use darkfi_sdk::{
crypto::{note::AeadEncryptedNote, pasta_prelude::*, MerkleNode, Nullifier, PublicKey},
crypto::{note::AeadEncryptedNote, pasta_prelude::*, MerkleNode, Nullifier, PublicKey, poseidon_hash, TokenId},
error::ContractError,
pasta::pallas,
};
@@ -29,6 +29,40 @@ use darkfi_serial::{SerialDecodable, SerialEncodable};
use darkfi_serial::async_trait;
use darkfi_sdk::crypto::{ShareAddress, ShareAddressType};
/// DAOs are represented on chain as a commitment to this object
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
pub struct Dao {
pub proposer_limit: u64,
pub quorum: u64,
pub approval_ratio_quot: u64,
pub approval_ratio_base: u64,
pub gov_token_id: TokenId,
pub public_key: PublicKey,
pub bulla_blind: pallas::Base,
}
impl Dao {
pub fn to_bulla(&self) -> DaoBulla {
let proposer_limit = pallas::Base::from(self.proposer_limit);
let quorum = pallas::Base::from(self.quorum);
let approval_ratio_quot = pallas::Base::from(self.approval_ratio_quot);
let approval_ratio_base = pallas::Base::from(self.approval_ratio_base);
let (pub_x, pub_y) = self.public_key.xy();
let bulla = poseidon_hash::<8>([
proposer_limit,
quorum,
approval_ratio_quot,
approval_ratio_base,
self.gov_token_id.inner(),
pub_x,
pub_y,
self.bulla_blind,
]);
DaoBulla(bulla)
}
}
/// A `DaoBulla` represented in the state
#[derive(Debug, Copy, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
pub struct DaoBulla(pallas::Base);
@@ -77,6 +111,31 @@ impl TryInto<DaoBulla> for ShareAddress {
}
}
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
pub struct DaoProposal {
pub dest: PublicKey,
pub amount: u64,
pub token_id: TokenId,
pub dao_bulla: DaoBulla,
pub blind: pallas::Base,
}
impl DaoProposal {
pub fn to_bulla(&self) -> DaoProposalBulla {
let (dest_x, dest_y) = self.dest.xy();
let amount = pallas::Base::from(self.amount);
let bulla = poseidon_hash::<6>([
dest_x,
dest_y,
amount,
self.token_id.inner(),
self.dao_bulla.inner(),
self.blind,
]);
DaoProposalBulla(bulla)
}
}
/// A `DaoProposalBulla` represented in the state
#[derive(Debug, Copy, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
pub struct DaoProposalBulla(pallas::Base);

View File

@@ -18,9 +18,10 @@
use darkfi::Result;
use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
use darkfi_money_contract::model::CoinParams;
use darkfi_dao_contract::{
client::{DaoInfo, DaoVoteNote},
model::DaoBlindAggregateVote,
client::DaoVoteNote,
model::{Dao, DaoBlindAggregateVote},
};
use darkfi_sdk::{
crypto::{pasta_prelude::Field, pedersen_commitment_u64, DAO_CONTRACT_ID, DARK_TOKEN_ID},
@@ -67,7 +68,7 @@ fn integration_test() -> Result<()> {
// DAO parameters
let dao_keypair = th.holders.get(&Holder::Dao).unwrap().keypair;
let dao = DaoInfo {
let dao = Dao {
proposer_limit: 100_000_000,
quorum: 199_999_999,
approval_ratio_base: 2,
@@ -176,6 +177,20 @@ fn integration_test() -> Result<()> {
// TODO: Is it possible for an invalid transfer() to be constructed on exec()?
// Need to look into this.
info!("[Alice] Building DAO proposal tx");
// These coins are passed around to all DAO members who verify its validity
// They check
let coins = vec![
CoinParams {
public_key: th.holders.get(&Holder::Rachel).unwrap().keypair.public,
value: PROPOSAL_AMOUNT,
token_id: drk_token_id,
serial: pallas::Base::random(&mut OsRng),
spend_hook: pallas::Base::ZERO,
user_data: pallas::Base::ZERO,
}
];
let (propose_tx, propose_params, propose_info) = th.dao_propose(
&Holder::Alice,
&Holder::Rachel,

View File

@@ -20,8 +20,8 @@ use std::time::Instant;
use darkfi::{tx::Transaction, Result};
use darkfi_dao_contract::{
client::{DaoExecCall, DaoInfo, DaoProposalInfo},
model::{DaoBulla, DaoExecParams},
client::DaoExecCall,
model::{Dao, DaoProposal, DaoBulla, DaoExecParams},
DaoFunction, DAO_CONTRACT_ZKAS_DAO_EXEC_NS,
};
use darkfi_money_contract::{
@@ -45,9 +45,9 @@ impl TestHarness {
#[allow(clippy::too_many_arguments)]
pub fn dao_exec(
&mut self,
dao: &DaoInfo,
dao: &Dao,
dao_bulla: &DaoBulla,
proposal: &DaoProposalInfo,
proposal: &DaoProposal,
yes_vote_value: u64,
all_vote_value: u64,
yes_vote_blind: pallas::Scalar,

View File

@@ -20,7 +20,7 @@ use std::time::Instant;
use darkfi::{tx::Transaction, Result};
use darkfi_dao_contract::{
client, client::DaoInfo, model::DaoMintParams, DaoFunction, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
client, model::{Dao, DaoMintParams}, DaoFunction, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
};
use darkfi_sdk::{
crypto::{Keypair, MerkleNode, DAO_CONTRACT_ID},
@@ -34,7 +34,7 @@ use super::{Holder, TestHarness, TxAction};
impl TestHarness {
pub fn dao_mint(
&mut self,
dao_info: &DaoInfo,
dao_info: &Dao,
dao_kp: &Keypair,
) -> Result<(Transaction, DaoMintParams)> {
let (dao_mint_pk, dao_mint_zkbin) =

View File

@@ -20,8 +20,8 @@ use std::time::Instant;
use darkfi::{tx::Transaction, Result};
use darkfi_dao_contract::{
client::{DaoInfo, DaoProposalInfo, DaoProposeCall, DaoProposeStakeInput},
model::{DaoBulla, DaoProposeParams},
client::{DaoProposeCall, DaoProposeStakeInput},
model::{Dao, DaoProposal, DaoBulla, DaoProposeParams},
DaoFunction, DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
};
use darkfi_money_contract::client::OwnCoin;
@@ -42,9 +42,9 @@ impl TestHarness {
recipient: &Holder,
amount: u64,
tx_token_id: TokenId,
dao: &DaoInfo,
dao: &Dao,
dao_bulla: &DaoBulla,
) -> Result<(Transaction, DaoProposeParams, DaoProposalInfo)> {
) -> Result<(Transaction, DaoProposeParams, DaoProposal)> {
let wallet = self.holders.get(proposer).unwrap();
let (dao_propose_burn_pk, dao_propose_burn_zkbin) =
@@ -74,10 +74,12 @@ impl TestHarness {
signature_secret,
};
let proposal = DaoProposalInfo {
let proposal = DaoProposal {
dest: self.holders.get(recipient).unwrap().keypair.public,
amount,
token_id: tx_token_id,
// TODO: pass proposal in directly
dao_bulla: dao.to_bulla(),
blind: pallas::Base::random(&mut OsRng),
};

View File

@@ -20,8 +20,8 @@ use std::time::Instant;
use darkfi::{tx::Transaction, Result};
use darkfi_dao_contract::{
client::{DaoInfo, DaoProposalInfo, DaoVoteCall, DaoVoteInput},
model::{DaoProposalBulla, DaoVoteParams},
client::{DaoVoteCall, DaoVoteInput},
model::{Dao, DaoProposal, DaoProposalBulla, DaoVoteParams},
DaoFunction, DAO_CONTRACT_ZKAS_DAO_VOTE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS,
};
use darkfi_money_contract::client::OwnCoin;
@@ -41,8 +41,8 @@ impl TestHarness {
voter: &Holder,
dao_kp: &Keypair,
vote_option: bool,
dao: &DaoInfo,
proposal: &DaoProposalInfo,
dao: &Dao,
proposal: &DaoProposal,
proposal_bulla: &DaoProposalBulla,
) -> Result<(Transaction, DaoVoteParams)> {
let wallet = self.holders.get(voter).unwrap();