drk: dao records sympliefied and dao mint call fee added

Removed dao_id and use the dao name as the identifier. Cleaned up all dao record structures. Fixed dao mint call and added its fee.
This commit is contained in:
skoupidi
2024-05-22 16:43:03 +03:00
parent 1cbddfdf93
commit 10ea889e31
6 changed files with 377 additions and 514 deletions

View File

@@ -25,7 +25,7 @@
-- $ drk dao import_dao DAO_NAME < dao.dat
-- Imported DAO ccb8XXX8af6
--
-- Where ccb8XXX8af6 is the DAO's bulla.
-- Where ccb8XXX8af6 is the DAO's name.
--
-- Next one person will mint it on chain
--
@@ -104,22 +104,16 @@
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_daos (
dao_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name BLOB UNIQUE NOT NULL,
proposer_limit BLOB NOT NULL,
-- minimum threshold for total number of votes for proposal to pass.
-- If there's too little activity then it cannot pass.
quorum BLOB NOT NULL,
-- Needed ratio of yes/total for proposal to pass.
-- approval_ratio = approval_ratio_quot / approval_ratio_base
approval_ratio_base INTEGER NOT NULL,
approval_ratio_quot INTEGER NOT NULL,
gov_token_id BLOB NOT NULL,
secret BLOB NOT NULL,
bulla_blind BLOB NOT NULL,
-- these values are NULL until the DAO is minted on chain and received
leaf_position BLOB,
-- Name identifier of the DAO
name TEXT PRIMARY KEY UNIQUE NOT NULL,
-- DAO parameters
params BLOB NOT NULL,
-- These values are NULL until the DAO is minted on chain and received
-- Leaf position of the DAO in the Merkle tree of DAOs
leaf_position BLOB,
-- The transaction hash where the DAO was deployed
tx_hash BLOB,
-- The call index in the transaction where the DAO was deployed
call_index INTEGER
);
@@ -131,7 +125,7 @@ CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_tree
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_proposals (
proposal_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
dao_id INTEGER NOT NULL,
dao_name TEXT NOT NULL,
-- Public key of person that would receive the funds
recv_public BLOB NOT NULL,
-- Amount of funds that would be sent
@@ -141,15 +135,15 @@ CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_prop
bulla_blind BLOB NOT NULL,
-- these values are NULL until the proposal is minted on chain
-- and received by the DAO
leaf_position BLOB,
money_snapshot_tree BLOB,
leaf_position BLOB,
money_snapshot_tree BLOB,
tx_hash BLOB,
call_index INTEGER,
-- this is NULL until we have voted on this proposal
our_vote_id INTEGER UNIQUE,
FOREIGN KEY(our_vote_id) REFERENCES Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_votes(vote_id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(dao_id) REFERENCES Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_daos(dao_id) ON DELETE CASCADE ON UPDATE CASCADE
FOREIGN KEY(dao_name) REFERENCES Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_daos(name) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_votes (

View File

@@ -264,24 +264,23 @@ pub fn generate_completions(shell: &str) -> Result<()> {
let name = Arg::with_name("name").help("Name identifier for the DAO");
let import =
SubCommand::with_name("import").about("Import DAO data from stdin").args(&vec![name]);
let import = SubCommand::with_name("import")
.about("Import DAO data from stdin")
.args(&vec![name.clone()]);
let name = Arg::with_name("dao-alias").help("Name identifier for the DAO (optional)");
let opt_name = Arg::with_name("dao-alias").help("Name identifier for the DAO (optional)");
let list = SubCommand::with_name("list")
.about("List imported DAOs (or info about a specific one)")
.args(&vec![name]);
let dao_alias = Arg::with_name("dao-alias").help("Name or numeric identifier for the DAO");
.args(&vec![opt_name]);
let balance = SubCommand::with_name("balance")
.about("Show the balance of a DAO")
.args(&vec![dao_alias.clone()]);
.args(&vec![name.clone()]);
let mint = SubCommand::with_name("mint")
.about("Mint an imported DAO on-chain")
.args(&vec![dao_alias.clone()]);
.args(&vec![name.clone()]);
let recipient =
Arg::with_name("recipient").help("Pubkey to send tokens to with proposal success");
@@ -292,17 +291,16 @@ pub fn generate_completions(shell: &str) -> Result<()> {
let propose = SubCommand::with_name("propose")
.about("Create a proposal for a DAO")
.args(&vec![dao_alias.clone(), recipient, amount, token]);
.args(&vec![name.clone(), recipient, amount, token]);
let proposals = SubCommand::with_name("proposals")
.about("List DAO proposals")
.args(&vec![dao_alias.clone()]);
let proposals =
SubCommand::with_name("proposals").about("List DAO proposals").args(&vec![name.clone()]);
let proposal_id = Arg::with_name("proposal-id").help("Numeric identifier for the proposal");
let proposal = SubCommand::with_name("proposal")
.about("View a DAO proposal data")
.args(&vec![dao_alias.clone(), proposal_id.clone()]);
.args(&vec![name.clone(), proposal_id.clone()]);
let vote = Arg::with_name("vote").help("Vote (0 for NO, 1 for YES)");
@@ -310,7 +308,7 @@ pub fn generate_completions(shell: &str) -> Result<()> {
Arg::with_name("vote-weight").help("Vote weight (amount of governance tokens)");
let vote = SubCommand::with_name("vote").about("Vote on a given proposal").args(&vec![
dao_alias.clone(),
name.clone(),
proposal_id.clone(),
vote,
vote_weight,
@@ -318,7 +316,7 @@ pub fn generate_completions(shell: &str) -> Result<()> {
let exec = SubCommand::with_name("exec")
.about("Execute a DAO proposal")
.args(&vec![dao_alias, proposal_id]);
.args(&vec![name, proposal_id]);
let dao = SubCommand::with_name("dao").about("DAO functionalities").subcommands(vec![
create, view, import, list, balance, mint, propose, proposals, proposal, vote, exec,

File diff suppressed because it is too large Load Diff

View File

@@ -335,20 +335,20 @@ enum DaoSubcmd {
/// Show the balance of a DAO
Balance {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
},
/// Mint an imported DAO on-chain
Mint {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
},
/// Create a proposal for a DAO
Propose {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
/// Pubkey to send tokens to with proposal success
recipient: String,
@@ -362,14 +362,14 @@ enum DaoSubcmd {
/// List DAO proposals
Proposals {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
},
/// View a DAO proposal data
Proposal {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
/// Numeric identifier for the proposal
proposal_id: u64,
@@ -377,8 +377,8 @@ enum DaoSubcmd {
/// Vote on a given proposal
Vote {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
/// Numeric identifier for the proposal
proposal_id: u64,
@@ -392,8 +392,8 @@ enum DaoSubcmd {
/// Execute a DAO proposal
Exec {
/// Name or numeric identifier for the DAO
dao_alias: String,
/// Name identifier for the DAO
name: String,
/// Numeric identifier for the proposal
proposal_id: u64,
@@ -1039,9 +1039,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
};
let secret_key = SecretKey::random(&mut OsRng);
let bulla_blind = pallas::Base::random(&mut OsRng);
let bulla_blind = BaseBlind::random(&mut OsRng);
let params = DaoParams {
let params = DaoParams::new(
proposer_limit,
quorum,
approval_ratio_base,
@@ -1049,7 +1049,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
gov_token_id,
secret_key,
bulla_blind,
};
);
let encoded = bs58::encode(&serialize_async(&params).await).into_string();
println!("{encoded}");
@@ -1074,7 +1074,6 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
let params: DaoParams = deserialize_async(&bytes).await?;
let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
if let Err(e) = drk.import_dao(&name, params).await {
eprintln!("Failed to import DAO: {e:?}");
exit(2);
@@ -1093,11 +1092,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
Ok(())
}
DaoSubcmd::Balance { dao_alias } => {
DaoSubcmd::Balance { name } => {
let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
let balmap = match drk.dao_balance(dao_id).await {
let balmap = match drk.dao_balance(&name).await {
Ok(b) => b,
Err(e) => {
eprintln!("Failed to fetch DAO balance: {e:?}");
@@ -1139,23 +1136,22 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
Ok(())
}
DaoSubcmd::Mint { dao_alias } => {
DaoSubcmd::Mint { name } => {
let drk =
Drk::new(args.wallet_path, args.wallet_pass, Some(args.endpoint), ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
let tx = match drk.dao_mint(dao_id).await {
let tx = match drk.dao_mint(&name).await {
Ok(tx) => tx,
Err(e) => {
eprintln!("Failed to mint DAO: {e:?}");
exit(2);
}
};
println!("{}", base64::encode(&serialize_async(&tx).await));
Ok(())
}
DaoSubcmd::Propose { dao_alias, recipient, amount, token } => {
DaoSubcmd::Propose { name, recipient, amount, token } => {
if let Err(e) = f64::from_str(&amount) {
eprintln!("Invalid amount: {e:?}");
exit(2);
@@ -1171,7 +1167,6 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
let drk =
Drk::new(args.wallet_path, args.wallet_pass, Some(args.endpoint), ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
let token_id = match drk.get_token(token).await {
Ok(t) => t,
Err(e) => {
@@ -1180,7 +1175,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
}
};
let tx = match drk.dao_propose(dao_id, rcpt, amount, token_id).await {
let tx = match drk.dao_propose(&name, rcpt, amount, token_id).await {
Ok(tx) => tx,
Err(e) => {
eprintln!("Failed to create DAO proposal: {e:?}");
@@ -1191,11 +1186,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
Ok(())
}
DaoSubcmd::Proposals { dao_alias } => {
DaoSubcmd::Proposals { name } => {
let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
let proposals = drk.get_dao_proposals(dao_id).await?;
let proposals = drk.get_dao_proposals(&name).await?;
for proposal in proposals {
println!("[{}] {:?}", proposal.id, proposal.bulla());
@@ -1204,11 +1197,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
Ok(())
}
DaoSubcmd::Proposal { dao_alias, proposal_id } => {
DaoSubcmd::Proposal { name, proposal_id } => {
let drk = Drk::new(args.wallet_path, args.wallet_pass, None, ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
let proposals = drk.get_dao_proposals(dao_id).await?;
let proposals = drk.get_dao_proposals(&name).await?;
let Some(proposal) = proposals.iter().find(|x| x.id == proposal_id) else {
eprintln!("No such DAO proposal found");
exit(2);
@@ -1226,11 +1217,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
Ok(())
}
DaoSubcmd::Vote { dao_alias, proposal_id, vote, vote_weight } => {
let drk =
Drk::new(args.wallet_path, args.wallet_pass, Some(args.endpoint), ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
DaoSubcmd::Vote { name, proposal_id, vote, vote_weight } => {
if let Err(e) = f64::from_str(&vote_weight) {
eprintln!("Invalid vote weight: {e:?}");
exit(2);
@@ -1243,7 +1230,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
}
let vote = vote != 0;
let tx = match drk.dao_vote(dao_id, proposal_id, vote, weight).await {
let drk =
Drk::new(args.wallet_path, args.wallet_pass, Some(args.endpoint), ex).await?;
let tx = match drk.dao_vote(&name, proposal_id, vote, weight).await {
Ok(tx) => tx,
Err(e) => {
eprintln!("Failed to create DAO Vote transaction: {e:?}");
@@ -1258,11 +1247,10 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
Ok(())
}
DaoSubcmd::Exec { dao_alias, proposal_id } => {
DaoSubcmd::Exec { name, proposal_id } => {
let drk =
Drk::new(args.wallet_path, args.wallet_pass, Some(args.endpoint), ex).await?;
let dao_id = drk.get_dao_id(&dao_alias).await?;
let dao = drk.get_dao_by_id(dao_id).await?;
let dao = drk.get_dao_by_name(&name).await?;
let proposal = drk.get_dao_proposal_by_id(proposal_id).await?;
assert!(proposal.dao_bulla == dao.bulla());

View File

@@ -65,7 +65,7 @@ of the guide can be added for future regressions.
| 20 | DAO view | dao view | Pass |
| 21 | DAO import | dao import | Pass |
| 22 | DAO list | dao list | Pass |
| 23 | DAO mint | dao mint {DAO} | Failure: needs #19 |
| 23 | DAO mint | dao mint {DAO} | Pass |
| 24 | DAO balance | dao balance {DAO} | Failure: needs #19 |
| 25 | DAO propose | dao propose {DAO} {ADDR} {AMOUNT} {TOKEN} | Failure: needs #19 |
| 26 | DAO proposals retrieval | dao proposals {DAO} | Failure: needs #25 |

View File

@@ -37,12 +37,18 @@ use darkfi_serial::async_trait;
// ANCHOR: dao
/// DAOs are represented on chain as a commitment to this object
pub struct Dao {
/// 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_quot: u64,
pub approval_ratio_base: u64,
/// DAO's governance token ID
pub gov_token_id: TokenId,
/// Public key of the DAO
pub public_key: PublicKey,
/// DAO bulla blind
pub bulla_blind: BaseBlind,
}
// ANCHOR_END: dao