Files
darkfi/bin/dao/daod/demo-spec.md

9.2 KiB

title, author
title author
DAO demo architecture jstark

This document outlines a simple demo to showcase the smart contract schema underlying the initial DAO MVP. We have tried to mimic the basic DarkFi architecture while remaining as simple as possible.

We do not have a blockchain, p2p network, or encrypted wallet database in this highly simplified demo. It is just a local network of 4 nodes and a relayer. The values are all stored in memory.

Layers

bin Located in darkfi/bin/dao.

  • daod/: receives rpc requests and operates a client.
  • dao-cli/: command-line interface that receives input and sends rpc requests.
  • relayerd/: receives transactions on TCP and relays them to all nodes.

src Located in darkfi/bin/dao/daod.

  • contract/: source code for dao and money contracts.
  • util/: demo-wide utilities.
  • state/: stores dao and money states.
  • tx: underlying types required by transactions, function calls and call data.
  • node/: a dao full node.

node A dao node containing client and wallet submodules.

  • client/: operates a wallet and performs state transition and validate methods.
  • wallet/: owns and operates secret values.

proof Located in darkfi/bin/dao/proof. Contains the zk proofs.

Command Flow

The following assumes that a user has already compiled the zk contracts by running make.

This requires a Makescript as follows:

ZK_SRC_FILES := $(wildcard proof/*.zk)
ZK_BIN_FILES := $(patsubst proof/%.zk, proof/%.zk.bin, $(ZK_SRC_FILES))

daod: $(ZK_BIN_FILES)
	cargo run --release

proof/%.zk.bin: proof/%.zk
	zkas $<

We will also need to write a shell script that opens 9 terminals and runs the following:

Terminal 1: relayerd. Terminal 2-5: 4 instances of daod. Terminal 6-9: 4 instances of dao-cli.

Relayerd and daod should be sent to the background, so the demo will consist visually of 4 terminals running dao-cli.

Start relayer

  1. relayerd starts a listener for all TCP ports specified in config.

Initialize DAO

Note: this happens automatically on daod first run.

  1. daod: starts a listener on the relayer TCP port.
  2. daod: creates a client and calls client.init().
  3. client: creates a money wallet.
  4. money-wallet: generates cashier, faucet keys.
  5. client: gets public keys from wallet and calls state.new().
  6. state: creates ZkContractTable, StateRegistry.
  7. state: loads all zk binaries and saves them in ZkContractTable.
  8. state: creates a new money/dao state and registers them in StateRegistry.

Stage 1: create DAO

  1. dao-cli: sends create() rpc request to daod.
  2. daod: receives rpc request and calls client.create().
  3. client: creates a dao wallet.
  4. dao-wallet: specifies the dao params.
  5. dao-wallet: creates a dao keypair, bulla blind, and signature secret.

build sequence.

Note: Builders differ according to the FuncCall, but the basic sequence is the same.

  1. dao-wallet: build: creates dao_contract::mint::wallet::Builder.
  2. dao-wallet: generates a FuncCall from builder.build().
  3. dao-wallet: adds FuncCall to a vector.
  4. dao-wallet: sign the vector of FuncCalls.

send sequence.

  1. dao-wallet: create a Transaction.
  2. dao-wallet: send the Transaction to the relayer.
  3. relayer: receives a Transaction on one of its connections.
  4. relayer: relays the Transaction to all connected nodes.

recv sequence.

  1. daod: receives a Transaction on its relayerd listener.
  2. 'daod`: sends the Transaction to Client.

validate sequence.

  1. client: validate: creates an empty vector of updates.
  2. client: loops through all FuncCalls in the Transaction.
  3. client: runs a match statement on the FUNC_ID.
  4. client: finds mint FUNC_ID and runs a state transition function.
  5. client: pushes the result to Vec
  6. client: outside the loop, atomically applies all updates.
  7. client: calls zk_verify() on the Transaction.
  8. client: verifies signatures.

  1. client: sends Transaction to the relayer.
  2. relayer: receives Transaction and relays.
  • TODO: dao-wallet: waits until Transction is confirmed. (how?)
  1. dao-wallet: look up the dao state and call witness().
  2. dao-wallet: get the dao bulla from the Transaction.
  3. dao-cli: print "Created DAO {}".

Stage 2: fund DAO

  • TODO: for the demo it might be better to call mint() first and then fund(), passing the values into fund()

Here we are creating a treasury token and sending it to the DAO.

  1. dao-cli: fund() rpc request to daod
  2. daod: receives rpc request and calls client.fund().
  3. client: creates treasury token, random token ID and supply

Note: dao-wallet must manually track coins to retrieve coins belonging to its private key.

  1. dao-wallet: looks up the money state, and calls state.wallet_cache.track()

  2. money-wallet: sets spend hook to dao_contract::exec::FUNC_ID

  3. money-wallet: sets user_data to dao_bulla

  • TODO: how does it get the dao_bulla? Must be stored somewhere.
  1. money-wallet: specifies dao public key and treasury token BuilderOutputInfo.
  2. money-wallet: runs the build sequence for money::transfer.
  3. money-wallet: create Transaction and send.
  4. relayer: receives Transaction and relays.
  5. daod: receives a Transaction and sends to client.
  6. client: runs the validate sequence.

Note: here we get all coins associated with the private key. 13. dao-wallet: looks up the state and calls WalletCache.get_received() 14. dao-wallet: check the coin is valid by recreating Coin 15. daod: sendswith token ID and balance to dao-cli. 16. dao-cli: displays data using pretty table.

Stage 3: airdrop

  1. dao-cli: calls keygen()
  2. daod: client.keygen()
  3. daod: money-wallet.keygen()
  4. money-wallet: creates new keypair
  5. money-wallet: looks up the money_contract State and calls WalletCache.track()
  6. money-wallet: return the public key
  7. dao-cli: prints the public key

Note: do this 3 times to generate 3 pubkey keys for different daod instances.

  1. dao-cli: calls mint()
  2. daod: call client.mint()
  3. client: creates governance token with random token ID and supply
  4. dao-cli: prints "created token {} with supply {}"
  5. dao-cli: calls airdrop() and passes a value and a pubkey.
  6. dao-wallet: runs the build sequence for money::transfer.
  7. dao-wallet: create Transaction and send.
  8. relayer: receives Transaction and relays.
  9. daod: receives a Transaction and sends to client.
  10. client: runs the validate sequence.
  11. money-wallet: state.wallet_cache.get_received()
  12. money-wallet: check the coin is valid by recreating Coin
  13. daod: sends token ID and balance to cli
  14. dao-cli: prints "received coin {} with value {}".
  • TODO: money-wallet must keep track of Coins and have a flag for whether or not they are spent.
  • Hashmap of <Coin, bool> ?

Stage 4: create proposal

  • TODO: maybe for the demo we should just hardcode a user/ proposal recipient.
  1. dao-cli: calls propose() and enter a user pubkey and an amount
  2. dao-wallet: runs the build sequence for dao_contract::propose
  3. dao-wallet: specifies user pubkey, amount and token ID in Proposal
  4. dao-cli: prints "Created proposal to send {} xDRK to {}"
  5. dao-wallet: create Transaction and send.
  6. relayer: receives Transaction and relays.
  7. daod: receives a Transaction and sends to client.
  8. client: runs the validate sequence.
  • TODO: how does everyone have access to DAO private key?
  1. dao-wallet: reads received proposal and tries to decrypt Note
  2. dao-wallet: sends decrypted values to daod
  3. dao-cli: prints "Proposal is now active"

Stage 5 vote

  1. dao-cli: calls vote() and enters a vote option (yes or no) and an amount
  2. daod: calls client.vote()
  3. money-wallet: get money_leaf_position and money_merkle_path
  4. money-wallet: create builder sequence for dao_contract::vote
  5. money-wallet: specify dao_keypair in vote_keypair field
  • TODO: this implies that money-wallet is able to access private values in dao-wallet
  1. money-wallet: signs and sends
  2. relayer: receives Transaction and relays.
  3. daod: receives a Transaction and sends to client.
  4. client: runs the validate sequence.
  5. dao-wallet: tries to decrypt the Vote.
  6. dao-cli: prints "Received vote {} value {}"

Note: repeat 3 times with different values and vote options.

  • TODO: ignore section re: vote commitments?
  • TODO: determine outcome: yes_votes_value/ all_votes_value e.g. when the quorum is reached, print "Quorum reached! Outcome {}" or just hardcode it for X n. of voters

Stage 6: Executing the proposal

  1. dao-cli: calls exec()
  • TODO: how does dao have access to user data?
  1. dao-wallet: get money_leaf_position and money_merkle_path
  2. dao-wallet: specifies user_kaypair and proposal amount in 1st output
  3. dao-wallet: specifies change in 2nd output
  4. dao-wallet: run build sequence for money_contract::transfer
  5. dao-wallet: run build sequence for dao_contract::exec
  6. dao-wallet: signs transaction and sends
  7. relayer: receives Transaction and relays.
  8. daod: receives a Transaction and sends to client.
  9. client: runs the validate sequence.