mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
contract/dao: allow exec call only after voting period has passed
This commit is contained in:
@@ -2269,6 +2269,12 @@ impl Drk {
|
||||
// Fetch our money Merkle tree
|
||||
let tree = self.get_money_tree().await?;
|
||||
|
||||
// Retrieve next block height and current block time target,
|
||||
// to compute their window.
|
||||
let next_block_height = self.get_next_block_height().await?;
|
||||
let block_target = self.get_block_target().await?;
|
||||
let current_blockwindow = blockwindow(next_block_height, block_target);
|
||||
|
||||
// Now we can create the transfer call parameters
|
||||
let input_user_data_blind = Blind::random(&mut OsRng);
|
||||
let mut inputs = vec![];
|
||||
@@ -2320,6 +2326,7 @@ impl Drk {
|
||||
yes_vote_blind,
|
||||
all_vote_blind,
|
||||
signature_secret: exec_signature_secret,
|
||||
current_blockwindow,
|
||||
};
|
||||
let (exec_params, exec_proofs) = exec_builder.make(&dao_exec_zkbin, &dao_exec_pk)?;
|
||||
|
||||
|
||||
@@ -77,11 +77,11 @@ $ ./drk dao balance MiladyMakerDAO
|
||||
|
||||
Now that the DAO has something in its treasury, we can generate a
|
||||
transfer proposal to send it somewhere, that will be up to vote
|
||||
for 30 block periods. Let's propose to send 5 of the 10 tokens to
|
||||
for 1 block period. Let's propose to send 5 of the 10 tokens to
|
||||
our address (we can find that with `drk wallet --address`):
|
||||
|
||||
```
|
||||
$ ./drk dao propose-transfer MiladyMakerDAO 30 5 WCKD {YOUR_ADDRESS}
|
||||
$ ./drk dao propose-transfer MiladyMakerDAO 1 5 WCKD {YOUR_ADDRESS}
|
||||
```
|
||||
|
||||
After command was executed, it will output the generated proposal
|
||||
@@ -146,10 +146,10 @@ current status when running `dao proposal {PROPOSAL_BULLA}`.
|
||||
|
||||
## Executing the proposal
|
||||
|
||||
Once enough votes have been cast that meet the required minimum (quorum)
|
||||
and assuming the yes:no votes ratio is bigger than the approval ratio,
|
||||
then we are ready to confirm the vote. Any DAO member can perform this
|
||||
action.
|
||||
Once the block period has passed and enough votes have been cast that
|
||||
meet the required minimum (quorum), and assuming the yes:no votes ratio
|
||||
ratio is bigger than the approval ratio, then we are ready to confirm
|
||||
the vote. Any DAO member can perform this action.
|
||||
|
||||
Since in our tutorial the `MLDY` governance tokens we used surpass the
|
||||
quorum, we can execute the proposal right away:
|
||||
@@ -203,7 +203,7 @@ from the DAO treasury to the new DAO we created:
|
||||
|
||||
```
|
||||
$ ./drk dao list WickedDAO
|
||||
$ ./drk dao propose-transfer MiladyMakerDAO 30 6.9 MLDY {WICKED_DAO_PUBLIC_KEY} \
|
||||
$ ./drk dao propose-transfer MiladyMakerDAO 1 6.9 MLDY {WICKED_DAO_PUBLIC_KEY} \
|
||||
{DAO_CONTRACT_SPEND_HOOK} {WICKED_DAO_BULLA}
|
||||
$ ./drk dao proposal {PROPOSAL_BULLA} --mint-proposal > dao_mldy_transfer_proposal_wckd_mint_tx
|
||||
$ ./drk broadcast < dao_mldy_transfer_proposal_wckd_mint_tx
|
||||
@@ -216,7 +216,7 @@ $ ./drk dao vote {PROPOSAL_BULLA} 1 > dao_mldy_transfer_proposal_wckd_vote_tx
|
||||
$ ./drk broadcast < dao_mldy_transfer_proposal_wckd_vote_tx
|
||||
```
|
||||
|
||||
And execute it:
|
||||
And execute it, after the vote period(1 block period) has passed:
|
||||
|
||||
```
|
||||
$ ./drk dao exec {PROPOSAL_BULLA} > dao_mldy_transfer_proposal_wckd_exec_tx
|
||||
|
||||
@@ -31,6 +31,9 @@ witness "Exec" {
|
||||
Scalar yes_vote_blind,
|
||||
Scalar all_vote_blind,
|
||||
|
||||
# Check whether the proposal has expired or not
|
||||
Base current_blockwindow,
|
||||
|
||||
# Signature secret
|
||||
Base signature_secret,
|
||||
}
|
||||
@@ -61,6 +64,11 @@ circuit "Exec" {
|
||||
constrain_instance(proposal_bulla);
|
||||
constrain_instance(proposal_auth_calls_commit);
|
||||
|
||||
# Enforce that the proposal has expired
|
||||
end_time = base_add(proposal_creation_blockwindow, proposal_duration_blockwindows);
|
||||
less_than_strict(end_time, current_blockwindow);
|
||||
constrain_instance(current_blockwindow);
|
||||
|
||||
# Create Pedersen commitments for win_votes and total_votes, and
|
||||
# constrain the commitments' coordinates.
|
||||
yes_vote_value_c = ec_mul_short(yes_vote_value, VALUE_COMMIT_VALUE);
|
||||
|
||||
@@ -46,7 +46,7 @@ circuit "VoteMain" {
|
||||
token_commit = poseidon_hash(dao_gov_token_id, gov_token_blind);
|
||||
constrain_instance(token_commit);
|
||||
|
||||
# cast to EcPoint
|
||||
# Cast to EcPoint
|
||||
# (otherwise zkas refuses to compile)
|
||||
ONE = witness_base(1);
|
||||
dao_pubkey = ec_mul_var_base(ONE, dao_public_key);
|
||||
|
||||
@@ -43,6 +43,7 @@ pub struct DaoExecCall {
|
||||
pub yes_vote_blind: ScalarBlind,
|
||||
pub all_vote_blind: ScalarBlind,
|
||||
pub signature_secret: SecretKey,
|
||||
pub current_blockwindow: u64,
|
||||
}
|
||||
|
||||
impl DaoExecCall {
|
||||
@@ -77,8 +78,10 @@ impl DaoExecCall {
|
||||
|
||||
let signature_public = PublicKey::from_secret(self.signature_secret);
|
||||
|
||||
let current_blockwindow = pallas::Base::from(self.current_blockwindow);
|
||||
|
||||
let prover_witnesses = vec![
|
||||
// proposal params
|
||||
// Proposal params
|
||||
Witness::Base(Value::known(proposal_auth_calls_commit)),
|
||||
Witness::Base(Value::known(pallas::Base::from(self.proposal.creation_blockwindow))),
|
||||
Witness::Base(Value::known(pallas::Base::from(self.proposal.duration_blockwindows))),
|
||||
@@ -93,12 +96,14 @@ impl DaoExecCall {
|
||||
Witness::Base(Value::known(dao_pub_x)),
|
||||
Witness::Base(Value::known(dao_pub_y)),
|
||||
Witness::Base(Value::known(self.dao.bulla_blind.inner())),
|
||||
// votes
|
||||
// Votes
|
||||
Witness::Base(Value::known(pallas::Base::from(self.yes_vote_value))),
|
||||
Witness::Base(Value::known(pallas::Base::from(self.all_vote_value))),
|
||||
Witness::Scalar(Value::known(self.yes_vote_blind.inner())),
|
||||
Witness::Scalar(Value::known(self.all_vote_blind.inner())),
|
||||
// signature secret
|
||||
// Time checks
|
||||
Witness::Base(Value::known(current_blockwindow)),
|
||||
// Signature secret
|
||||
Witness::Base(Value::known(self.signature_secret.inner())),
|
||||
];
|
||||
|
||||
@@ -106,6 +111,7 @@ impl DaoExecCall {
|
||||
let public_inputs = vec![
|
||||
proposal_bulla.inner(),
|
||||
proposal_auth_calls_commit,
|
||||
current_blockwindow,
|
||||
*yes_vote_commit_coords.x(),
|
||||
*yes_vote_commit_coords.y(),
|
||||
*all_vote_commit_coords.x(),
|
||||
|
||||
@@ -251,8 +251,9 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
|
||||
let (ephem_x, ephem_y) = ephem_pubkey.xy();
|
||||
|
||||
let current_blockwindow = pallas::Base::from(self.current_blockwindow);
|
||||
|
||||
let prover_witnesses = vec![
|
||||
// proposal params
|
||||
// Proposal params
|
||||
Witness::Base(Value::known(self.proposal.auth_calls.commit())),
|
||||
Witness::Base(Value::known(pallas::Base::from(self.proposal.creation_blockwindow))),
|
||||
Witness::Base(Value::known(pallas::Base::from(self.proposal.duration_blockwindows))),
|
||||
@@ -272,9 +273,9 @@ impl<T: StorageAdapter<Value = pallas::Base>> DaoVoteCall<'_, T> {
|
||||
// Total number of gov tokens allocated
|
||||
Witness::Base(Value::known(all_vote_value_fp)),
|
||||
Witness::Base(Value::known(all_vote_blind.inner())),
|
||||
// gov token
|
||||
// Gov token
|
||||
Witness::Base(Value::known(gov_token_blind)),
|
||||
// time checks
|
||||
// Time checks
|
||||
Witness::Base(Value::known(current_blockwindow)),
|
||||
// verifiable encryption
|
||||
Witness::Base(Value::known(ephem_secret.inner())),
|
||||
|
||||
@@ -27,6 +27,7 @@ use darkfi_sdk::{
|
||||
use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
|
||||
|
||||
use crate::{
|
||||
blockwindow,
|
||||
error::DaoError,
|
||||
model::{DaoExecParams, DaoExecUpdate, DaoProposalMetadata, VecAuthCallCommit},
|
||||
DaoFunction, DAO_CONTRACT_DB_PROPOSAL_BULLAS, DAO_CONTRACT_ZKAS_DAO_EXEC_NS,
|
||||
@@ -46,6 +47,9 @@ pub(crate) fn dao_exec_get_metadata(
|
||||
// Public keys for the transaction signatures we have to verify
|
||||
let signature_pubkeys: Vec<PublicKey> = vec![params.signature_public];
|
||||
|
||||
let current_blockwindow =
|
||||
blockwindow(wasm::util::get_verifying_block_height()?, wasm::util::get_block_target()?);
|
||||
|
||||
let blind_vote = params.blind_total_vote;
|
||||
let yes_vote_coords = blind_vote.yes_vote_commit.to_affine().coordinates().unwrap();
|
||||
let all_vote_coords = blind_vote.all_vote_commit.to_affine().coordinates().unwrap();
|
||||
@@ -55,6 +59,7 @@ pub(crate) fn dao_exec_get_metadata(
|
||||
vec![
|
||||
params.proposal_bulla.inner(),
|
||||
params.proposal_auth_calls.commit(),
|
||||
pallas::Base::from(current_blockwindow),
|
||||
*yes_vote_coords.x(),
|
||||
*yes_vote_coords.y(),
|
||||
*all_vote_coords.x(),
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use darkfi::Result;
|
||||
use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
|
||||
use darkfi_dao_contract::{
|
||||
blockwindow,
|
||||
model::{Dao, DaoBlindAggregateVote, DaoVoteParams},
|
||||
DaoFunction,
|
||||
};
|
||||
@@ -57,6 +58,7 @@ const PROPOSER_LIMIT: u64 = 100_000_000;
|
||||
const QUORUM: u64 = 200_000_000;
|
||||
const APPROVAL_RATIO_BASE: u64 = 2;
|
||||
const APPROVAL_RATIO_QUOT: u64 = 1;
|
||||
const PROPOSAL_DURATION_BLOCKWINDOW: u64 = 1;
|
||||
// The tokens we want to send via the transfer proposal
|
||||
const TRANSFER_PROPOSAL_AMOUNT: u64 = 250_000_000;
|
||||
|
||||
@@ -320,6 +322,11 @@ async fn execute_transfer_proposal(
|
||||
blind: Blind::random(&mut OsRng),
|
||||
}];
|
||||
|
||||
// Grab creation blockwindow
|
||||
let block_target =
|
||||
th.holders.get_mut(&Holder::Dao).unwrap().validator.consensus.module.read().await.target;
|
||||
let creation_blockwindow = blockwindow(*current_block_height, block_target);
|
||||
|
||||
let (tx, params, fee_params, proposal_info) = th
|
||||
.dao_propose_transfer(
|
||||
&Holder::Alice,
|
||||
@@ -327,6 +334,7 @@ async fn execute_transfer_proposal(
|
||||
user_data,
|
||||
dao,
|
||||
*current_block_height,
|
||||
PROPOSAL_DURATION_BLOCKWINDOW,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -396,7 +404,6 @@ async fn execute_transfer_proposal(
|
||||
.await?;
|
||||
}
|
||||
th.assert_trees(&HOLDERS);
|
||||
*current_block_height += 1;
|
||||
|
||||
// Gather and decrypt all generic vote notes
|
||||
let vote_note_1 = alice_vote_params.note.decrypt_unsafe(&dao_keypair.secret).unwrap();
|
||||
@@ -411,6 +418,13 @@ async fn execute_transfer_proposal(
|
||||
(vote_note_3, charlie_vote_params),
|
||||
]);
|
||||
|
||||
// Wait until proposal has expired
|
||||
let mut current_blockwindow = creation_blockwindow;
|
||||
while current_blockwindow <= creation_blockwindow + PROPOSAL_DURATION_BLOCKWINDOW + 1 {
|
||||
*current_block_height += 1;
|
||||
current_blockwindow = blockwindow(*current_block_height, block_target);
|
||||
}
|
||||
|
||||
// ================
|
||||
// Dao::Exec
|
||||
// Execute the vote
|
||||
@@ -472,8 +486,21 @@ async fn execute_generic_proposal(
|
||||
// Propose the vote
|
||||
// ================
|
||||
info!("[Alice] Building DAO generic proposal tx");
|
||||
let (tx, params, fee_params, proposal_info) =
|
||||
th.dao_propose_generic(&Holder::Alice, user_data, dao, *current_block_height).await?;
|
||||
|
||||
// Grab creation blockwindow
|
||||
let block_target =
|
||||
th.holders.get_mut(&Holder::Dao).unwrap().validator.consensus.module.read().await.target;
|
||||
let creation_blockwindow = blockwindow(*current_block_height, block_target);
|
||||
|
||||
let (tx, params, fee_params, proposal_info) = th
|
||||
.dao_propose_generic(
|
||||
&Holder::Alice,
|
||||
user_data,
|
||||
dao,
|
||||
*current_block_height,
|
||||
PROPOSAL_DURATION_BLOCKWINDOW,
|
||||
)
|
||||
.await?;
|
||||
|
||||
for holder in &HOLDERS {
|
||||
info!("[{holder:?}] Executing DAO generic proposal tx");
|
||||
@@ -541,7 +568,6 @@ async fn execute_generic_proposal(
|
||||
.await?;
|
||||
}
|
||||
th.assert_trees(&HOLDERS);
|
||||
*current_block_height += 1;
|
||||
|
||||
// Gather and decrypt all generic vote notes
|
||||
let vote_note_1 = alice_vote_params.note.decrypt_unsafe(&dao_keypair.secret).unwrap();
|
||||
@@ -556,6 +582,13 @@ async fn execute_generic_proposal(
|
||||
(vote_note_3, charlie_vote_params),
|
||||
]);
|
||||
|
||||
// Wait until proposal has expired
|
||||
let mut current_blockwindow = creation_blockwindow;
|
||||
while current_blockwindow <= creation_blockwindow + PROPOSAL_DURATION_BLOCKWINDOW + 1 {
|
||||
*current_block_height += 1;
|
||||
current_blockwindow = blockwindow(*current_block_height, block_target);
|
||||
}
|
||||
|
||||
// ================
|
||||
// Dao::Exec
|
||||
// Execute the vote
|
||||
|
||||
@@ -21,6 +21,7 @@ use darkfi::{
|
||||
Result,
|
||||
};
|
||||
use darkfi_dao_contract::{
|
||||
blockwindow,
|
||||
client::{DaoAuthMoneyTransferCall, DaoExecCall},
|
||||
model::{Dao, DaoProposal},
|
||||
DaoFunction, DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_ENC_COIN_NS,
|
||||
@@ -146,6 +147,8 @@ impl TestHarness {
|
||||
xfer_params.inputs.iter().map(|input| input.value_commit).sum()
|
||||
);
|
||||
|
||||
let block_target = dao_wallet.validator.consensus.module.read().await.target;
|
||||
let current_blockwindow = blockwindow(block_height, block_target);
|
||||
let exec_builder = DaoExecCall {
|
||||
proposal: proposal.clone(),
|
||||
dao: dao.clone(),
|
||||
@@ -154,6 +157,7 @@ impl TestHarness {
|
||||
yes_vote_blind,
|
||||
all_vote_blind,
|
||||
signature_secret: exec_signature_secret,
|
||||
current_blockwindow,
|
||||
};
|
||||
|
||||
let (exec_params, exec_proofs) = exec_builder.make(dao_exec_zkbin, dao_exec_pk)?;
|
||||
@@ -251,11 +255,15 @@ impl TestHarness {
|
||||
all_vote_blind: ScalarBlind,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
|
||||
let (dao_exec_pk, dao_exec_zkbin) =
|
||||
self.proving_keys.get(DAO_CONTRACT_ZKAS_DAO_EXEC_NS).unwrap();
|
||||
|
||||
// Create the exec call
|
||||
let exec_signature_secret = SecretKey::random(&mut OsRng);
|
||||
let block_target = wallet.validator.consensus.module.read().await.target;
|
||||
let current_blockwindow = blockwindow(block_height, block_target);
|
||||
let exec_builder = DaoExecCall {
|
||||
proposal: proposal.clone(),
|
||||
dao: dao.clone(),
|
||||
@@ -264,6 +272,7 @@ impl TestHarness {
|
||||
yes_vote_blind,
|
||||
all_vote_blind,
|
||||
signature_secret: exec_signature_secret,
|
||||
current_blockwindow,
|
||||
};
|
||||
let (exec_params, exec_proofs) = exec_builder.make(dao_exec_zkbin, dao_exec_pk)?;
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ impl TestHarness {
|
||||
user_data: pallas::Base,
|
||||
dao: &Dao,
|
||||
block_height: u32,
|
||||
duration_blockwindows: u64,
|
||||
) -> Result<(Transaction, DaoProposeParams, Option<MoneyFeeParamsV1>, DaoProposal)> {
|
||||
let wallet = self.holders.get(proposer).unwrap();
|
||||
|
||||
@@ -121,7 +122,7 @@ impl TestHarness {
|
||||
let proposal = DaoProposal {
|
||||
auth_calls,
|
||||
creation_blockwindow,
|
||||
duration_blockwindows: 30,
|
||||
duration_blockwindows,
|
||||
user_data,
|
||||
dao_bulla: dao.to_bulla(),
|
||||
blind: Blind::random(&mut OsRng),
|
||||
@@ -193,6 +194,7 @@ impl TestHarness {
|
||||
user_data: pallas::Base,
|
||||
dao: &Dao,
|
||||
block_height: u32,
|
||||
duration_blockwindows: u64,
|
||||
) -> Result<(Transaction, DaoProposeParams, Option<MoneyFeeParamsV1>, DaoProposal)> {
|
||||
let wallet = self.holders.get(proposer).unwrap();
|
||||
|
||||
@@ -238,7 +240,7 @@ impl TestHarness {
|
||||
let proposal = DaoProposal {
|
||||
auth_calls: vec![],
|
||||
creation_blockwindow,
|
||||
duration_blockwindows: 30,
|
||||
duration_blockwindows,
|
||||
user_data,
|
||||
dao_bulla: dao.to_bulla(),
|
||||
blind: Blind::random(&mut OsRng),
|
||||
|
||||
@@ -49,8 +49,8 @@ use sled_overlay::sled;
|
||||
|
||||
/// Update these if any circuits are changed.
|
||||
/// Delete the existing cachefiles, and enable debug logging, you will see the new hashes.
|
||||
const PKS_HASH: &str = "e8de97d286a4a31606f96dfd13bb5a6e9dfa49322573b8cd1fe936aee7e33e58";
|
||||
const VKS_HASH: &str = "aa59b5e53c10c994c127beb443d6b1b4c21ee7417ce1a4f717c82431b7b8c8d9";
|
||||
const PKS_HASH: &str = "55d9535b390a819026bb3554f5de501997b831935bbc910951639fddfec44b14";
|
||||
const VKS_HASH: &str = "cbcb356ccacd0ad4ac953417f07bf24297927e61ba770f5aeddaa9e72f159272";
|
||||
|
||||
/// Build a `PathBuf` to a cachefile
|
||||
fn cache_path(typ: &str) -> Result<PathBuf> {
|
||||
|
||||
Reference in New Issue
Block a user