# Privagon - building Private DAOs
#### Links:
- [Github Repository](https://github.com/gabrielfior/dao-private-voting2/)
- [Presentation](https://docs.google.com/presentation/d/15y_6fAVkUoTbhez8tthjAH7KIDtEXNBRIjZpO3mHflU/edit?usp=sharing)
## Idea
We want to build the first True DAO. Were the code is law, and yet there exists a private state that only the DAO members can access, such as proposals, passwords to internal accounts, etc. This solution does not require DAO members to maintain and share ad-hock public/private key, but rather depends on the integrity of the Zama MPC computatoins.
As part of the project, we demonstrate:
- DAO can have **private membership** Only DAO members know who is a member of a DAO.
- DAO can create **private proposal**, content of which is only accessable to DAO members.
- DAOs can share **private credentials**, like software access keys, only accessible to current DAO members.
Additionally, we build a quick prototype of private voting system, that is powering decisions in the DAO.
## Motivation
As discussed in [this work](https://www.icloud.com/iclouddrive/039xUDKvT_B3Q9afrSm6logOg#project) and [this presentation](https://docs.google.com/presentation/d/1A5fMlz4zPpyDZ802wBxFgJdNN3w3oGl5ANE_TGtfX0c/edit?usp=sharing) today DAOs are very limited. They have to choose to either be fully transparent, heavily limiting their activity and causing cases like with [Constitution DAO](https://www.constitutiondao.com), or have an elected commite that would share a secret used to create a private DAO space.
The downsides of the first approach are evident. If we want to make DAOs thrive, we need to enable them to make confidencial desisions and discussions. Else how would a DAO maintain an Twitter Account password, accissible to any of its members?
The downside of the second approach is also clear. If the password to the Twitter Account is maintain by a single person, they can just reject to give it to anyone, centralising the power.
So createing a solution where you can achieve both **Access Decentralisation** and **DAO Private Space** is vital and revolutionary to the space. There have been [attempts](https://github.com/ElusAegis/Enclave-PSSC) at that already, however they relied on inherently centralised technology, [Intel SGX](https://www.intel.com/content/www/us/en/architecture-and-technology/software-guard-extensions.html).
### Nous Research Sprint
In addition to the above mentioned, we were very curious to find out how hard would it be to fullfill the [Nouns Private Voting Research Sprint](https://nouns.wtf/vote/216) requirements using Zama tool stack, originally intended for 3 months. This sprint has recieved more than 10 proposals from [top ZK & Cryptography teams](https://prop.house/nouns/private-voting-research-sprint), including Aragon, Aztech, O(1), Axiom and many more.
Though we have expcuded several requirements for simplicity, such as replacing ERC721 with ERC20, and removing Delegation for now, we have kept the key ones:
1. Vote Secrecy
2. Vote Fairness
3. Weighted Voting
4. No Double Voting
Our following work proved that Zama tool stack is extermelly useful for the outset task.
## Description
The system allows DAO members to:
1. Create Proposal
2. Retreave the Proposal Content
3. Vote in the Proposal
### Create Proposal
Proposals are created by DAO members and can have encrypted or public content.
To submit an encrypted proposal, the proposer generates an AES encryption key, encrypts the proposal content with it and publishes it to the IPFS.
*Note: this is done to reduce the computation costs of decryption on-chain, as symetric key cryptography is much cheaper.*
The obtained IPFS hash and the AES encryption key are then passed to the `createProposal` function in the DAO Smart Contract. The AES encryption key is passed as a private value. The function returns the proposal id.
If the proposal is intended to be public, one can share the AES encryption key.
### Retreive the Proposal Content
Before voting, the DAO members should familiarise themselves with the proposal content.
To do so, they will request the AES Proposal access key from the DAO Smart Contraact. This method will check if they are a DAO member and if so, return them the key.
With the key, the DAO member will be able to decrypt the Proposal IPFS file and make their mind.
### Vote in the Proposal
Once the voter has made their mind, they can vote For/Agaist/Abstain in the proposal. To do so, they select the proposalId and submit an encrypted vote. We use their token balance as their wieght.
*Note: to prevent double voting, we checkpoint the balances at the proposal creation moment, and use them as the wieght.
### Get the result
Once the proposal is finished, we will evluate the result and return if the proposal passed or not. Note that due to the nature of Zama blockchain, the result is unknown until the end, so in addition to maintaining vote privacy, we also get vote fairness.
## Specification
### CLI
*Language: TypeScript*
#### Create Proposal
Script that:
1. Generates a Proposal Encryption AES Key
2. Encrypts the recieved Proposal Text
3. Pins the Proposal to the IPFS
4. Calls `uploadSecret` on DAO Smart Contract to store the secret AES256 Key inside the DAO. This returns the `secretStorageId`.
5. Calls `createProposal` on DAO Smart Contract with the public`ipfsProposalHash` and the `secretStorageId`.
6. Returns the Proposal ID
*Inputs: Proposal Text
Outputs: Proposal Id, Proposal IPFS Link*
```bash!
>>> zama-dao create-proposal "Lets spend up to 100,000$ to buy the copy of USA Constitution."
Proposal 1 successfully created! Ipfs hash is `ipfs:///ipfs/QmdmQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7RgQm`
```
#### Retrieve Proposal
Script that:
1. Fetches Encrypted Proposal Text from IPFS
2. Retrieves the AES key by first calling `getProposalKeySecretId` on DAO smart contract to get the storage location of the key.
3. Then call the `getSecret` function with the key storage location to get the AES key. This requires the authentication signature.
4. Decrypts the Proposal Content with the AES Key.
*Inputs: Proposal Id,
Outputs: Proposal Text*
```bash!
>>> zama-dao retrive-proposal 1
"Lets spend up to 100,000$ to buy the copy of USA Constitution."
```
#### Vote
Script that:
1. Calls the `vote` function to submit the vote for a provided `proposalId`.
*Inputs: Proposal Id, Vote Choice
Outputs: **None***
```bash!
>>> zama-dao vote 1 yes
Submitted your vote choice!
```
#### storeSecret
Script that:
1. Calls the `storeSecret` function from smart contract to store a given secret `my-secret`.
*Inputs: secret
Outputs: secretId
```bash!
>>> zama-dao storeSecret "Github Account Password: Ae4v5qw"
42
```
#### getSecret
Script that:
1. Calls the `getSecret` function from smart contract to submit the vote for a provided `proposalId`.
*Inputs: secretId
Outputs: encrypted secret ID
```bash!
>>> zama-dao getSecret 42
"Github Account Password: Ae4v5qw"
```
### Secret Storage Contract
*Language: Solidity*
#### State
```solidity!
// The mapping between the id of the secret and the encrypted value
internal mapping(uint256->euint32) secretStorage
```
#### `_uploadSetcret`
Function that uploads a secret value and saves it to the mapping. It can store an arbitrary amount of `euint32` values and returns the range where it is stored.
```solidity!
_uploadSetcret(euint32[] memory secret) internal (uint256)
```
#### `_getSecret`
Funciton that returns the encrypted secret stored in the memory.
```solidity!
_getSecret(euint32[] memory processId) internal (euint32[] memory)
```
### DAO Contract
*Language: Solidity*
**Notation:**
*`auth` implise that we use the [ERC712](https://eips.ethereum.org/EIPS/eip-712) to make sure the caller is a particular address.*
*`enc` implise that we encr the value to the PublicKey of the caller.*
For simplisity our contract just extends the Private ERC20 token contract.
#### State
```solidity!
// Mapping between the proposalId and the content of the proposal
public mapping(uint256->Proposal) proposals;
// The id of the latest proposal
public uint256 latestPropsal;
// Min amount of tokens to be a member
internal euint32 minMemeberTokenBalance;
```
#### `isMember`
Public Accessible function that tells if the person who called this function is a DAO member or not. The result is encrypted.
```solidity!
isMember(address) view auth returns (enc boolean)
```
#### `_isMember`
An internal function without any authentication checks that returns an fhe boolean if a person is a DAO member or not. To do so, the function checks if the person has anough tokens, comparing users token balance and `minMemeberTokenBalance`.
```solidity!
isMember(address) internal view returns (eboolean)
```
#### `uploadSecret`
Function that uploads a secret that can be then fetched by DAO members.
```solidity!
uploadSecret(bytes32[] calldata secret) public returns(uint256 secretId)
```
#### `getSecret`
Function returns the decrypted secret that is stored in the provided `secretId`. The function calls `_isMember` to determine if the user is a member.
To access the value we call `_fetchSecret` with the key `secretId` and reencrypt it to the users public key.
```solidity!
getSecret(uint256 secretId) view auth returns (enc u32[])
```
#### `createProposal`
Function that creates a propsal with provided metadata and saves the metadata in the `proposals` mapping.
We pass the reference to the location in Storage where the AES Key is stored. This allows us to reuse the same key without increasing the storage size.
```solidity!
createProposal(uint256 keyStorageId, uint256 ipfsHash, uint64 endBlock) public returns(uint256 proposalId)
```
#### `getProposalKeySecretId`
Function returns the key `secretStorageId` for the `proposalId`. This can be used later to with the `getSecret` to get the secret.
```solidity!
getProposalKeySecretId(uint256 proposalId) view returns (uint256 keyStorageId)
```
#### `vote`
Function allows a user to cast a vote in the proposal.
The function supports the following vote options:
- *For(1 in uint32)*
- *Against(2 in uint32)*
- *Abstain(0 in uint32)*
The function multiplies the token balance of the voter with the selected vote option and updates the total vote count with it. It additionally updates the number of people voted.
```solidity!
vote(uint256 processId, calldata bytes voteOption) public returns()
```
#### `getResult`
Function that returns the result for the proposal. It must check that the end block has been reached. As currently decryption of results is unavailable on-chain, we will send reencrypted results to the DAO members for them to interpret it.
The function returns a tripplet of `(votesFor, votesAgainst, votesAbstain)`.
```solidity!
getResult(uint256 processId) public auth returns((enc uint32, enc uint32, enc uint32))
```
## Future Work
### Snapshot of Balances
We can use a [MiniMe](https://github.com/Giveth/minime) token standard or [storage proofs](https://www.axiom.xyz) to checkpoint balances to a particular block.
### AES256 Encrypt/Decrypt Library
A library to store the Encrypted AES Keys as well as return them to only those users that should have access to it.
### ASCII Encrypt/Decrypt Library
To provide a way to store the encrypted proposal on chain instead of the IPFS, thus simplifying the solution. This will be a library that will encrypt the provided String into ASCII.
**This likely requires forking the protocol**
### Membership Deniability
We can make it impossible to prove if you are a member of a DAO or not. This will protect users from the regulators, as there will be no way to prove who is liable or not.
This can be achived in two steps:
1. [Deniable Encryption](https://link.springer.com/chapter/10.1007/BFb0052229) of the Proposal.
A person will encrypt a proposal such that there will be two decryption keys: X, Y. Using the X key, you get a correct version of the proposal. Using key Y, you get a arbitrary proposal, that looks legitimate enough. This creates a hidden DAO inside of a DAO. The key Y will be shared with a group of people who are actually not DAO members.
2. Everyone should be able to weight.
- Those who can get real proposal X and a actually eligible to vote, will vote as usual.
- Those who saw the key Y will also be able to cast a vote, however there vote will not be counted towards the total result.