HackMD
  • Beta
    Beta  Get a sneak peek of HackMD’s new design
    Turn on the feature preview and give us feedback.
    Go → Got it
      • Create new note
      • Create a note from template
    • Beta  Get a sneak peek of HackMD’s new design
      Beta  Get a sneak peek of HackMD’s new design
      Turn on the feature preview and give us feedback.
      Go → Got it
      • Sharing Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • More (Comment, Invitee)
      • Publishing
        Please check the box to agree to the Community Guidelines.
        Everyone on the web can find and read all notes of this public team.
        After the note is published, everyone on the web can find and read this note.
        See all published notes on profile page.
      • Commenting Enable
        Disabled Forbidden Owners Signed-in users Everyone
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Invitee
      • No invitee
      • Options
      • Versions and GitHub Sync
      • Transfer ownership
      • Delete this note
      • Template
      • Save as template
      • Insert from template
      • Export
      • Dropbox
      • Google Drive Export to Google Drive
      • Gist
      • Import
      • Dropbox
      • Google Drive Import from Google Drive
      • Gist
      • Clipboard
      • Download
      • Markdown
      • HTML
      • Raw HTML
    Menu Sharing Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Versions and GitHub Sync Transfer ownership Delete this note
    Export
    Dropbox Google Drive Export to Google Drive Gist
    Import
    Dropbox Google Drive Import from Google Drive Gist Clipboard
    Download
    Markdown HTML Raw HTML
    Back
    Sharing
    Sharing Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    More (Comment, Invitee)
    Publishing
    Please check the box to agree to the Community Guidelines.
    Everyone on the web can find and read all notes of this public team.
    After the note is published, everyone on the web can find and read this note.
    See all published notes on profile page.
    More (Comment, Invitee)
    Commenting Enable
    Disabled Forbidden Owners Signed-in users Everyone
    Permission
    Owners
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Invitee
    No invitee
       owned this note    owned this note      
    Published Linked with GitHub
    Like BookmarkBookmarked
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- Title: How to Build a Private DAO on Ethereum breaks: false --- $$ \def\Zq{\mathbb{Z}_q} \def\EE{\mathbb{E}} \def\deq{\mathrel{\mathop:}=} \def\range#1{[{#1}]} \def\PK{\textit{pk}} \def\SK{\textit{sk}} \def\ct{\textit{ct}} $$ # How to Build a Private DAO on Ethereum ### Griffin Dunaif and Dan Boneh --- A [DAO is a form of partnership](https://en.wikipedia.org/wiki/Decentralized_autonomous_organization) where a group of people come together to achieve a shared goal. Recently, a DAO called [ConstitutionDAO](https://en.wikipedia.org/wiki/ConstitutionDAO) formed with the mission of purchasing one of the last remaining physical copies of the Constitution. The DAO raised an astonishing $40 million in under a week to place a bid. However, the DAO was promptly outbid by an individual investor who placed a bid at $43.17 million, just slightly above the assets of ConsutitutionDAO, thereby ensuring that the investor won the auction. Herein lies the problem with the current state of DAOs: their treasuries are public. When ConstitutionDAO was raising money, its entire balance sheet was visible to the public. This property can be detrimental to DAOs and impairs their ability to participate in certain types of auctions. In particular, DAOs cannot participate in sealed bid auctions precisely because their treasuries are public. In the case of ConstitutionDAO, having a public treasury enabled other bidders to know its maximal bid and outbid it. If DAOs are to become widely adopted, particularly by businesses, it is essential that the assets of the DAOs remain private. In particular, only the DAO managers should know the total assets managed by the DAO. There are ample use cases where a private treasury is necessary: an investment DAO may want the size of the fund to remain private, a business may want to keep its current sales numbers private, and of course, a fund participating in auctions needs its treasury to remain private. To solve this issue, we propose a simple treasury design that enables DAOs to utilize private treasuries. This short post explains the inner workings of a private DAO that runs on the Ethereum network. Our protocol is somewhat related to a protocol used by an anonymity mixer called [Tornado.cash Classic](https://tornado.cash/), however the privacy properties of the two protocols are quite different. Most notably, in Tornado.cash the sender and receiver are the same person: a client deposits funds into the Tornado contract and that same client later withdraws the funds. In our settings the sender and receiver are different: anyone can send funds to the DAO, but only the DAO manager can withdraw the funds. We note that a more advanced version of Tornado.cash, called Nova, provides all the features we need. However, Nova is designed to solve a much harder problem. Our Private DAO protocol is much simpler than Nova, but offers a different privacy properties. ## Privacy Goals and high level design The plan is to deploy a single *master contract* on the Ethereum chain, and this one contract will manage many DAOs, similar to the design of [juicebox](https://juicebox.money/#/). This single contract acts as a platform that makes it easy to create and manage DAOs. Every DAO created on this platform has a designated *DAO manager*, or a designated group of managers. Anyone can send funds to a DAO on the platform. However, only the DAO manager can withdraw funds from the DAO it is managing. We aim to provide the following privacy properties: * **Balanace privacy**: When someone sends funds to a DAO managed by the master contract, an observer cannot tell which DAO the funds are for. Clearly, an observer can tell the aggregate amount submitted to all DAOs on the platform by reading the master contract's balance. However, an observer learns nothing about the balance of each DAO, other than this upper bound. * **The DAO manager**: The DAO manager can calculate the current balance of funds sent to its DAO. Moreover, the DAO manager should be able to prove to a 3rd party that the DAO's balance is greater than a certain amount. * **Withdrawal privacy**: When the DAO manager withdraws funds from its DAO, the amount withdrawn is public, but the remaining balance of the DAO remains hidden. * **Non goal:** We stress that we do not aim to provide privacy for end users who contributed funds to the DAO. In particular, an observer may eventually learn the Ethereum addresses who sent funds to the DAO. While we do not aim to provide privacy for contributors, our protocol does provide some privacy. We will come back to this when describing the withdrawal protocol. The last bullet needs more discussion. There are protocols that provide privacy for contributors, such as Tornado.cash Nova, where even the DAO manager does not know the source of the funds to its DAO. However, these designs may be difficult to use in the context of a DAO platform. First, privacy for contributors may complicate KYC requirements. Second, privacy for contributors may complicate the process of issuing refunds to contributors, in case the DAO needs to refund all or some of the contributions. Both issues are solvable, but they complicate the user experience. As such, we believe that our set of privacy goals outlined above is an interesting point in the design space, and one that enables a simple and efficient protocol. <!-- **Privacy for contributors**: The system should not reveal the source of the funds. An observer can see that a specific Ethereum address sent funds to the master contract. However, the observer should not know which DAO received the funds. Moreover, privacy for contributors should be maintained even after that DAO manager withdraws funds from the DAO. --> If the master contract manages sufficiently many DAOs of comparable size, then our privacy goals make it possible for DAOs on the platform to participate in a sealed bid auction. While an observer knows an upper bound on the assets of a particular DAO, it does not know the amount that belongs to that DAO. Moreover, the DAO manager can prove to the auction house that it has sufficient funds to participate in the auction, and can withdraw the funds when it comes time to bid. ## PDAO: A Private DAO Protocol The life cycle of a DAO managed on the platform has three key steps: * **DAO creation**: the DAO manager creates the DAO. This step require no on-chain transations. * **Deposit**: used by an end-user who wants to contrbute funds to the DAO. * **Withdrawal**: used by the DAO manager to withdraw funds from the DAO. We describe these steps in detail in the next three subsections. **Contract initialization**: When the master contract is first deployed it is initialized with an empty Merkle tree $T$ of depth $d$ (say $d = 32$). An empty tree is a tree where all the leaves are zero. The contract stores (i) the root hash of this Merkle tree, and (ii) a counter called *next* that is initially set to zero. This counter indicates the location of the next empty leaf in the tree, counting from left to right. When *next* reaches to $2^{d}-1$, the tree is full and a new contract must be initialized. ### Step 1: Creating a DAO Our protocol uses a finite cyclic group $\EE$ of prime order $q$ with generator $G \in \EE$. For efficiency, we take $\EE$ to be the group of points on the [Grumpkin elliptic curve](https://hackmd.io/@aztec-network/ByzgNxBfd#2-Grumpkin---A-curve-on-top-of-BN-254-for-SNARK-efficient-group-operations) that are defined over the base field. This curve is designed for efficient SNARK proof generation on the Ethereum network. The curve is defined over a prime field $\mathbb{F}_r$, where $r$ is the size of the curve *alt_bn128* used by the [Ethereum pairing precompile](https://eips.ethereum.org/EIPS/eip-197). As such, arithmetic over $\mathbb{F}_r$ can be efficiently implemented by a SNARK prover. Now, to create a DAO, the DAO manager posts a Schnorr public key to the DAO web site. In more detail, the DAO manager samples a random $\alpha$ from $\Zq$, computes $\PK \gets \alpha \cdot G \in \EE$, and posts $\PK$ to the DAO’s website. The manager keeps the secret key $\alpha$ to itself. That’s it. There is no on-chain activity and no gas fees. ### Step 2: A member sends funds to the DAO A client wants to send ETH to the DAO, say a total of $v$ ETH. The client obtains the DAO’s public key $\PK \in \EE$ from the DAO's web site and does the following: 1. Sample a random $\rho$ in $\Zq$; 1. Compute what we call a leaf, which is a pair: $\ \ L = \bigl( \rho \cdot G,\ \ \rho \cdot \PK \bigr) \in \EE^2$; 1. Call the master contract’s *deposit()* function with the argument $L$, sending $v$ ether in the transaction; 1. The master contract accepts the funds and records the expanded leaf $$L’ = \bigl( \rho \cdot G,\ \ \rho \cdot \PK,\ \ v, \ \ n \bigr)$$ in the next unoccupied leaf, from left to right, of the Merkle tree $T$. Here $n$ is the leaf number that is designated for $L'$ in $T$. Specifically, the contract sets $n \gets \textit{next}$ and inserts $L'$ into leaf number *next*. The contract increments the value of *next* by one, and computes the Merkle root of the tree $T$ obtained by adding this new leaf $L'$ to the existing tree $T$ at position $n$. If the tree has depth $d$, then computing the updated Merkle root can be done by storing only $d$ hash values in the contract's storage. Since the leaf $L$ is posted on-chain, and is public for the entire world to see, it is imperative that $L$ reveal nothing about the identity of the DAO that received the funds. In particular, nothing about $\PK$ should be revealed. This easily follows from the hardness of the Decision Diffie-Hellman assumption in the group $\EE$. ### Step 3: the DAO manager withdraws funds When the DAO manager wants to withdraw $w$ ETH from the DAO, it must do so by proving that the DAO was the intended recipient of at least that much ETH. Moreover, those funds should only be withdrawn once. All this must be done without revealing the balance of the DAO to the contract. We will accomplish this by utilizing a SNARK. First, how does the DAO manager even know the balance of the funds sent to the DAO? To do so, the DAO manager will monitor all the deposits sent to the master contract. For every observed leaf $L' = \bigl(P,\ Q,\ v,\ n \bigr)$ the DAO manager uses its secret key $\alpha$ to test if $$\alpha \cdot P = Q.$$ If so, the DAO manager learns that this deposit is intended for its DAO and records that $v$ ETH were added to the DAO balance. Otherwise, this deposit is meant for some other DAO. *A word of caution:* When the DAO manager finds a deposit meant for its DAO it will do a little more work to add the balance to the DAO balanace. It is important that this does not introduce a timing side channel on the DAO manager's machine, otherwise an observer will learn that the current deposit is meant for this DAO. [This paper](https://floriantramer.com/publications/sidechannels20/) discusses this problem in detail. **The withdrawal protocol.** Next, suppose the DAO manager wants to withdraw $w$ ETH from the DAO. We require that there is a subset of leaves $\bigl\{L_i' = (P_i, Q_i, v_i, n_i) \bigr\}_{i=1}^\ell$ that belong to the DAO such that $v_1 + \cdots + v_\ell = w$. A simple withdrawal protocol is for the DAO manager to call the *withdraw()* function on the master contract giving it the argument $$ \begin{align} \label{eq:one} (L_1', \ldots, L_\ell',\ \pi_m,\ \pi_s), \tag{1} \end{align} $$ where * $\pi_m$ is a batch-Merkle proof that proves that $L_1', \ldots, L_\ell'$ are leaves in the Merkle tree $T$ (recall that the master contract stores the Merkle root for $T$), * $\pi_s$ is a batch-Schnorr proof that proves that the DAO manager knows $\alpha \in \Zq$ such that $Q_i = \alpha P_i$ for all $i=1,\ldots,\ell$. Note that batch-Merkle and batch-Schnorr proofs are more efficient than simply repeating a single-instance Merkle or Schnorr proof $\ell$ times. The master contract verifies that 1. the proofs $\pi_m$ and $\pi_s$ are valid with respect to its stored Merkle root, 2. $v_1 + \cdots + v_\ell = w$, and 3. the leaves $n_1, \ldots, n_\ell \in [2^d]$ have not yet been spent. If so, the master contract sends $w$ ETH to the DAO manager, which is the originator of the withdrawal request. It also adds $n_1, \ldots, n_\ell$ to its list of spent leaves so that these leaves cannot be spent again. Note that by revealing which leaves are spent on withdrawal, an observer learns the Ethereum address of the contributor who sent the funds to the DAO. As such, a contributor Alice is anonymous until the point in time when the DAO manager withdraws Alice's contribution from the master contract. Prior to that moment, an observer will know that Alice contributed to the platform, but will not know which DAO received the contribution. **A more efficient withdrawal protocol.** The difficulty with the simple withdrawal protocol above is that the call data $(L_1', \ldots, L_\ell', \pi_m, \pi_s)$ in $\eqref{eq:one}$ can be quite large. Moreover, verifying the Merkle and Schnorr multi-proofs on chain can be quite costly. Both these factors can result in high transaction fees for withdrawing funds from the master contract. We can reduce the withdrawal transaction fees by using a SNARK. Here the DAO manager calls the *withdraw()* function on the master contract giving it the much shorter argument $$(n_1, \ldots, n_\ell, \pi),$$ where $\pi$ is a SNARK proof that the DAO manager knows a witness $$(\alpha, P_1, Q_1, v_1, \ldots, P_\ell, Q_\ell, v_\ell)$$ such that 1. for $i=1,\ldots,\ell$ the leaf $(P_i, Q_i, v_i, n_i)$ is in the merkle tree $T$, 2. for $i=1,\ldots,\ell$ we have $Q_i = \alpha \cdot P_i$, and 3. $v_1 + \cdots + v_\ell = w$. The master contract verifies the SNARK proof $\pi$ and checks that the leaves $n_1, \ldots, n_\ell \in [2^d]$ have not yet been spent. If so, the master contract sends $w$ ETH to the DAO manager, the originator of the withdrawal request. It then adds $n_1, \ldots, n_\ell$ to its list of spent leaves so that these leaves cannot be spent again. Since a SNARK requires a fixed size input, we can set $\ell = 100$, so that the DAO manager can withdraw a batch of up to a hundred leaves at a time. ## Implementation <!-- Our implementation of the system is available here: https://github.com/securitylab/privDAO --> The current implementation features a JavaScript CLI and package, which can be plugged into a graphical user interface. The JavaScript module consists of two functions: *deposit()* and *withdraw()*. The *withdraw()* function runs on the DAO manager's machine and computes a SNARK proof. This is done using the Groth16 SNARK system implemented in *snarkjs*. The PrivateTreasury smart contract is 145 lines of Solidity. It includes a SNARK verifier. In addition, it features the MerkleTreeWithHistory contract from TornadoCash, and the MiMC hash from circomlib.

    Import from clipboard

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lost their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template is not available.


    Upgrade

    All
    • All
    • Team
    No template found.

    Create custom template


    Upgrade

    Delete template

    Do you really want to delete this template?

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Tutorials

    Book Mode Tutorial

    Slide Mode Tutorial

    YAML Metadata

    Contacts

    Facebook

    Twitter

    Feedback

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions

    Versions and GitHub Sync

    Sign in to link this note to GitHub Learn more
    This note is not linked with GitHub Learn more
     
    Add badge Pull Push GitHub Link Settings
    Upgrade now

    Version named by    

    More Less
    • Edit
    • Delete

    Note content is identical to the latest version.
    Compare with
      Choose a version
      No search result
      Version not found

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub

        Please sign in to GitHub and install the HackMD app on your GitHub repo. Learn more

         Sign in to GitHub

        HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Available push count

        Upgrade

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Upgrade

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully