diff --git a/bin/drk/src/dao.rs b/bin/drk/src/dao.rs new file mode 100644 index 000000000..cad746ebb --- /dev/null +++ b/bin/drk/src/dao.rs @@ -0,0 +1,40 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2023 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use darkfi_sdk::{ + crypto::{SecretKey, TokenId}, + pasta::pallas, +}; +use darkfi_serial::{SerialDecodable, SerialEncodable}; + +#[derive(Debug, Clone, SerialEncodable, SerialDecodable)] +pub struct DaoParams { + /// The minimum amount of governance tokens needed to open a proposal + pub proposer_limit: u64, + /// Minimal threshold of participating total tokens needed for a proposal to pass + pub quorum: u64, + /// The ratio of winning/total votes needed for a proposal to pass + pub approval_ratio_base: u64, + pub approval_ratio_quot: u64, + /// DAO's governance token ID + pub gov_token_id: TokenId, + /// Secret key for the DAO + pub secret_key: SecretKey, + /// DAO bulla blind + pub bulla_blind: pallas::Base, +} diff --git a/bin/drk/src/main.rs b/bin/drk/src/main.rs index 53c3d49b4..98398a4d3 100644 --- a/bin/drk/src/main.rs +++ b/bin/drk/src/main.rs @@ -25,14 +25,15 @@ use std::{ use anyhow::{anyhow, Context, Result}; use clap::{Parser, Subcommand}; -use darkfi::tx::Transaction; +use darkfi::{tx::Transaction, zk::halo2::Field}; use darkfi_money_contract::client::Coin; use darkfi_sdk::{ - crypto::{PublicKey, TokenId}, + crypto::{PublicKey, SecretKey, TokenId}, pasta::{group::ff::PrimeField, pallas}, }; use darkfi_serial::{deserialize, serialize}; use prettytable::{format, row, Table}; +use rand::rngs::OsRng; use serde_json::json; use simplelog::{ColorChoice, TermLogger, TerminalMode}; use url::Url; @@ -66,6 +67,10 @@ mod rpc_wallet; mod cli_util; use cli_util::{parse_token_pair, parse_value_pair}; +/// DAO aux functionality +mod dao; +use dao::DaoParams; + #[derive(Parser)] #[command(about = cli_desc!())] struct Args { @@ -173,6 +178,10 @@ enum Subcmd { /// find coins sent to us and fill our wallet with the necessary metadata. Subscribe, + /// DAO functionalities + #[command(subcommand)] + Dao(DaoSubcmd), + /// Scan the blockchain and parse relevant transactions Scan { #[arg(long)] @@ -212,6 +221,92 @@ enum OtcSubcmd { Sign, } +#[derive(Subcommand)] +enum DaoSubcmd { + /// Create DAO parameters + Create { + /// The minimum amount of governance tokens needed to open a proposal for this DAO + proposer_limit: u64, + /// Minimal threshold of participating total tokens needed for a proposal to pass + quorum: u64, + /// The ratio of winning votes/total votes needed for a proposal to pass (2 decimals), + approval_ratio: f64, + /// DAO's governance token ID + gov_token_id: String, + }, + + /// View DAO data from stdin + View, + + /// List imported DAOs + List, + + /// Import DAO data from stdin + Import { + /// Named identifier for the DAO + dao_name: String, + }, + + /// Mint a DAO on-chain + Mint { + /// Named identifier for the DAO + dao_name: String, + }, + + /// Create a proposal for a DAO + Propose { + /// Named identifier for the DAO + dao_name: String, + + /// Pubkey to send tokens to with proposal success + recv_pubkey: String, + + /// Amount to send from DAO with proposal success + amount: u64, + + /// Token ID to send from DAO with proposal success + token_id: String, + + serial: u64, + }, + + /// List DAO proposals + Proposals { + /// Named identifier for the DAO + dao_name: String, + }, + + /// View a DAO proposal data + Proposal { + /// Named identifier for the DAO + dao_name: String, + + /// Proposal identifier + proposal: String, + }, + + /// Vote on a given proposal + Vote { + /// Named identifier for the DAO + dao_name: String, + + /// Proposal identifier + proposal: String, + + /// Vote + vote: String, + }, + + /// Execute a DAO proposal + Exec { + /// Named identifier for the DAO + dao_name: String, + + /// Proposal identifier + proposal: String, + }, +} + pub struct Drk { pub rpc_client: RpcClient, } @@ -592,5 +687,71 @@ async fn main() -> Result<()> { Ok(()) } + + Subcmd::Dao(cmd) => match cmd { + DaoSubcmd::Create { proposer_limit, quorum, approval_ratio, gov_token_id } => { + if approval_ratio > 1.0 { + eprintln!("Error: Approval ratio cannot be >1.0"); + exit(1); + } + + let approval_ratio_quot = 100_u64; + let approval_ratio_base = (approval_ratio * approval_ratio_quot as f64) as u64; + + let gov_token_id = + TokenId::try_from(gov_token_id.as_str()).with_context(|| "Invalid Token ID")?; + + let secret_key = SecretKey::random(&mut OsRng); + let bulla_blind = pallas::Base::random(&mut OsRng); + + let dao_params = DaoParams { + proposer_limit, + quorum, + approval_ratio_base, + approval_ratio_quot, + gov_token_id, + secret_key, + bulla_blind, + }; + + let encoded = bs58::encode(&serialize(&dao_params)).into_string(); + println!("{}", encoded); + Ok(()) + } + + DaoSubcmd::View => { + let mut buf = String::new(); + stdin().read_to_string(&mut buf)?; + let bytes = bs58::decode(&buf.trim()).into_vec()?; + let dao_params: DaoParams = deserialize(&bytes)?; + println!("DAO Parameters:"); + println!("Proposer limit: {}", dao_params.proposer_limit); + println!("Quorum: {}", dao_params.quorum); + println!( + "Approval ratio: {}", + dao_params.approval_ratio_base as f64 / dao_params.approval_ratio_quot as f64 + ); + println!("Governance token ID: {}", dao_params.gov_token_id); + println!("Secret key: {}", dao_params.secret_key); + println!("Bulla blind: {:?}", dao_params.bulla_blind); + Ok(()) + } + + DaoSubcmd::Import { dao_name } => todo!(), + + DaoSubcmd::List => todo!(), + + DaoSubcmd::Mint { dao_name } => todo!(), + + DaoSubcmd::Propose { dao_name, recv_pubkey, amount, token_id, serial } => todo!(), + + DaoSubcmd::Proposals { dao_name } => todo!(), + + DaoSubcmd::Proposal { dao_name, proposal } => todo!(), + + DaoSubcmd::Vote { dao_name, proposal, vote } => todo!(), + + DaoSubcmd::Exec { dao_name, proposal } => todo!(), + }, } }