Scheme

Let be defined as in the section PoseidonHash Function.

Let be defined as in the section Pallas and Vesta.

Let be defined as in the section Homomorphic Pedersen Commitments.

Let be defined as in the section Incremental Merkle Tree.

Let be defined as in DAO Model.

Let be defined as in In-band Secret Distribution.

Let be defined as in the section Verifiable In-Band Secret Distribution.

Mint

This function creates a DAO bulla . It's comparatively simple- we commit to the DAO params and then add the bulla to the set.

  • Wallet builder: src/contract/dao/src/client/mint.rs
  • WASM VM code: src/contract/dao/src/entrypoint/mint.rs
  • ZK proof: src/contract/dao/proof/dao-mint.zk

Function Params

Define the DAO mint function params

/// Parameters for `Dao::Mint`
pub struct DaoMintParams {
    /// The DAO bulla
    pub dao_bulla: DaoBulla,
    /// The DAO public key
    pub dao_pubkey: PublicKey,
}

Contract Statement

DAO bulla uniqueness   whether already exists. If yes then fail.

Let there be a prover auxiliary witness inputs:

Attach a proof such that the following relations hold:

Proof of public key ownership.

DAO bulla integrity

Signatures

There should be a single signature attached, which uses as the signature public key.

Propose

This contract function creates a DAO proposal. It takes a merkle root which contains the DAO bulla created in the Mint phase.

Several inputs are attached containing proof of ownership for the governance token. This is to satisfy the proposer limit value set in the DAO. We construct the nullifier which can leak anonymity when those same coins are spent. To workaround this, wallet implementers can attach an additional Money::transfer() call to the transaction.

The nullifier proves the coin isn't already spent in the set determined by . Each value commit exported by the input is summed and used in the main proof to determine the total value attached in the inputs crosses the proposer limit threshold.

This is merely a proof of ownership of holding a certain amount of value. Coins are not locked and continue to be spendable.

Additionally the encrypted note is used to send the proposal values to the DAO members using the public key set inside the DAO.

A proposal contains a list of auth calls as specified in Auth Calls. This specifies the contract call executed by the DAO on passing.

  • Wallet builder: src/contract/dao/src/client/propose.rs
  • WASM VM code: src/contract/dao/src/entrypoint/propose.rs
  • ZK proofs:
    • src/contract/dao/proof/dao-propose-main.zk
    • src/contract/dao/proof/dao-propose-input.zk

Function Params

Define the DAO propose function params

Define the DAO propose-input function params

/// Parameters for `Dao::Propose`
pub struct DaoProposeParams {
    /// Merkle root of the DAO in the DAO state
    pub dao_merkle_root: MerkleNode,
    /// Token ID commitment for the proposal
    pub token_commit: pallas::Base,
    /// Bulla of the DAO proposal
    pub proposal_bulla: DaoProposalBulla,
    /// Encrypted note
    pub note: AeadEncryptedNote,
    /// Inputs for the proposal
    pub inputs: Vec<DaoProposeParamsInput>,
}
/// Input for a DAO proposal
pub struct DaoProposeParamsInput {
    /// Value commitment for the input
    pub value_commit: pallas::Point,
    /// Merkle root for the input's coin inclusion proof
    pub merkle_coin_root: MerkleNode,
    /// SMT root for the input's nullifier exclusion proof
    pub smt_null_root: pallas::Base,
    /// Public key used for signing
    pub signature_public: PublicKey,
}

Contract Statement

Let be the current blockwindow as defined in Blockwindow.

Let be defined as in Coin.

Valid DAO bulla merkle root   check that is a previously seen merkle root in the DAO contract merkle roots DB.

Proposal bulla uniqueness   whether already exists. If yes then fail.

Let there be prover auxiliary witness inputs: Attach a proof such that the following relations hold:

Governance token commit   export the DAO token ID as an encrypted pedersen commit where .

DAO bulla integrity

DAO existence

Proposal bulla integrity where .

Proposer limit threshold met   check the proposer has supplied enough inputs that the required funds for the proposer limit set in the DAO is met. Let the total funds , then check .

Total funds value commit where . We use this to check that as claimed in the proposer limit threshold met check.

For each input , perform the following checks:

Unused nullifier   check that does not exist in the money contract nullifiers DB.

Valid input coins merkle root   check that is a previously seen merkle root in the money contract merkle roots DB.

  Let there be a prover auxiliary witness inputs:   Attach a proof such that the following relations hold:

Nullifier integrity

Coin value commit.

Token commit.

Valid coin   Check . Let . Check .

Proof of signature public key ownership.

Signatures

For each , attach a signature corresponding to the public key .

Vote

After DAO::propose() is called, DAO members can then call this contract function. Using a similar method as before, they attach inputs proving ownership of a certain value of governance tokens. This is how we achieve token weighted voting. The result of the vote is communicated to other DAO members through the encrypted note .

Each nullifier is stored uniquely per proposal. Additionally as before, there is a leakage here connecting the coins when spent. However prodigious usage of Money::transfer() to wash the coins after calling DAO::vote() should mitigate against this attack. In the future this can be fixed using set nonmembership primitives.

Another leakage is that the proposal bulla is public. To ensure every vote is discoverable by verifiers (who cannot decrypt values) and protect against 'nothing up my sleeve', we link them all together. This is so the final tally used for executing proposals is accurate.

The total sum of votes is represented by the commit and the yes votes by .

  • Wallet builder: src/contract/dao/src/client/vote.rs
  • WASM VM code: src/contract/dao/src/entrypoint/vote.rs
  • ZK proofs:
    • src/contract/dao/proof/dao-vote-main.zk
    • src/contract/dao/proof/dao-vote-input.zk

Function Params

Define the DAO vote function params

Define the DAO vote-input function params

Note: is a pedersen commitment, where the blinds are selected such that their sum is a valid field element in so the blind for can be verifiably encrypted. Likewise we do the same for the blind used to calculate .

This allows DAO members to securely receive all secrets for votes on a proposal. This is then used in the Exec phase when we work on the sum of DAO votes.

/// Parameters for `Dao::Vote`
pub struct DaoVoteParams {
    /// Token commitment for the vote inputs
    pub token_commit: pallas::Base,
    /// Proposal bulla being voted on
    pub proposal_bulla: DaoProposalBulla,
    /// Commitment for yes votes
    pub yes_vote_commit: pallas::Point,
    /// Encrypted note
    pub note: ElGamalEncryptedNote<4>,
    /// Inputs for the vote
    pub inputs: Vec<DaoVoteParamsInput>,
}
/// Input for a DAO proposal vote
pub struct DaoVoteParamsInput {
    /// Vote commitment
    pub vote_commit: pallas::Point,
    /// Vote nullifier
    pub vote_nullifier: Nullifier,
    /// Public key used for signing
    pub signature_public: PublicKey,
}

Contract Statement

Let be the current blockwindow as defined in Blockwindow.

Proposal bulla exists   check exists in the DAO contract proposal bullas DB.

Let there be prover auxiliary witness inputs: Attach a proof such that the following relations hold:

Governance token commit   export the DAO token ID as an encrypted pedersen commit where .

DAO bulla integrity

Proposal bulla integrity

Yes vote commit

Total vote value commit where should also hold.

Vote option boolean   enforce .

Proposal not expired   let , and then check .

Verifiable encryption of vote commit secrets   let , and verify .

For each input , perform the following checks:

Valid input merkle root   check that is the previously seen merkle root in the proposal snapshot merkle root.

Unused nullifier (money)   check that does not exist in the money contract nullifiers DB.

Unused nullifier (proposal)   check that does not exist in the DAO contract nullifiers DB for this specific proposal.

Let there be prover auxiliary witness inputs: Attach a proof such that the following relations hold:

Nullifier integrity

Coin value commit.

Token commit.

Valid coin   Check . Let . Check .

Proof of signature public key ownership.

Signatures

For each , attach a signature corresponding to the public key .

Exec

Exec is the final stage after voting is Accepted.

It checks the correct voting conditions have been met in accordance with the DAO params such as quorum and approval ratio. and are pedersen commits to and respectively.

It also checks that child calls have been attached in accordance with the auth calls set inside the proposal. One of these will usually be an auth module function. Currently the DAO provides a single preset for executing Money::transfer() calls so DAOs can manage anonymous treasuries.

  • Wallet builder: src/contract/dao/src/client/exec.rs
  • WASM VM code: src/contract/dao/src/entrypoint/exec.rs
  • ZK proof: src/contract/dao/proof/dao-exec.zk

Function Params

Let be defined as in the section Auth Calls.

Define the DAO exec function params

/// Parameters for `Dao::Exec`
pub struct DaoExecParams {
    /// The proposal bulla
    pub proposal_bulla: DaoProposalBulla,
    pub proposal_auth_calls: Vec<DaoAuthCall>,
    /// Aggregated blinds for the vote commitments
    pub blind_total_vote: DaoBlindAggregateVote,
    /// Public key for the signature.
    /// The signature ensures this DAO::exec call cannot be modified with other calls.
    pub signature_public: PublicKey,
}
/// Represents a single or multiple blinded votes.
/// These can be summed together.
pub struct DaoBlindAggregateVote {
    /// Weighted vote commit
    pub yes_vote_commit: pallas::Point,
    /// All value staked in the vote
    pub all_vote_commit: pallas::Point,
}

Contract Statement

There are two phases to Exec. In the first we check the calling format of this transaction matches what is specified in the proposal. Then in the second phase, we verify the correct voting rules.

Auth call spec match   denote the child calls of Exec by . If then exit. Otherwise, for each and , check the function ID of is .

Aggregate votes lookup   using the proposal bulla, fetch the aggregated votes from the DB and verify and are set correctly.

Let there be prover auxiliary witness inputs: Attach a proof such that the following relations hold:

DAO bulla integrity

Proposal bulla integrity where .

Yes vote commit

All vote commit

All votes pass quorum

Approval ratio satisfied   we wish to check that . Instead we perform the equivalent check that .

Signatures

No signatures are attached.

AuthMoneyTransfer

This is a child call for Exec which can be used for DAO treasuries. It checks the next sibling call is Money::transfer() and accordingly verifies the first output coins match the data set in this call's auth data.

Additionally we provide a note with the coin params that are verifiably encrypted to mitigate the attack where Exec is called, but the supplied Money::transfer() call contains an invalid note which cannot be decrypted by the receiver. In this case, the money would still leave the DAO treasury but be unspendable.

  • Wallet builder: src/contract/dao/src/client/auth_xfer.rs
  • WASM VM code: src/contract/dao/src/entrypoint/auth_xfer.rs
  • ZK proofs:
    • src/contract/dao/proof/dao-auth-money-transfer.zk
    • src/contract/dao/proof/dao-auth-money-transfer-enc-coin.zk

Function Params

Define the DAO AuthMoneyTransfer function params

This provides verifiable note encryption for all output coins in the sibling Money::transfer() call as well as the DAO change coin.

/// Parameters for `Dao::AuthMoneyTransfer`
pub struct DaoAuthMoneyTransferParams {
    pub enc_attrs: Vec<ElGamalEncryptedNote<5>>,
    pub dao_change_attrs: ElGamalEncryptedNote<3>,
}

Contract Statement

Denote the DAO contract ID by .

Sibling call is Money::transfer()   load the sibling call and check the contract ID and function code match Money::transfer().

Money originates from the same DAO   check all the input's user_data for the sibling Money::transfer() encode the same DAO. We do this by using the same blind for all user_data. Denote this value by .

Output coins match proposal   check there are output coins, with the first coins exactly matching those set in the auth data in the parent DAO::exec() call. Denote these proposal auth calls by .

Let there be a prover auxiliary witness inputs:

Attach a proof such that the following relations hold:

DAO bulla integrity

Proposal bulla integrity where matches the value in DAO::exec(), and .

Input user data commits to DAO bulla

DAO change coin integrity   denote the last coin in the Money::transfer() outputs by . Then check

Verifiable DAO change coin note encryption   let , and verify .

Then we do the same for each output coin of Money::transfer(). For , let and be the th output coin from Money::transfer(). Let there be prover auxiliary witness inputs: Attach a proof such that the following relations hold:

Coin integrity

Verifiable output coin note encryption   let , and verify .

Signatures

No signatures are attached.