|
|
@@ -1,4 +1,4 @@
|
|
|
-use std::{any::TypeId, sync::Arc, time::Instant};
|
|
|
+use std::{any::TypeId, collections::HashMap, sync::Arc, time::Instant};
|
|
|
|
|
|
use incrementalmerkletree::{Position, Tree};
|
|
|
use log::debug;
|
|
|
@@ -36,54 +36,33 @@ use crate::{
|
|
|
money_contract::{self, state::OwnCoin, transfer::Note},
|
|
|
},
|
|
|
rpc::JsonRpcInterface,
|
|
|
- util::{sign, FuncCall, StateRegistry, Transaction, ZkContractTable, GDRK_ID, XDRK_ID},
|
|
|
+ util::{
|
|
|
+ sign, FuncCall, HashableBase, StateRegistry, Transaction, ZkContractTable, GDRK_ID, XDRK_ID,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
-/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
-// TODO: restructure to this architecture.
|
|
|
-// Note: to make a Proposal, you need the dao_leaf_position
|
|
|
-// to make a Vote, you need the dao decryption key
|
|
|
-// Everyone has a unique money_wallet and a copy of the dao_wallet in their Client.
|
|
|
-//
|
|
|
-// pub struct Cashier {
|
|
|
-// cashier_wallet: CashierWallet,
|
|
|
-// zk_bins, ...
|
|
|
-// states ...
|
|
|
-// }
|
|
|
-//
|
|
|
-// impl Cashier {
|
|
|
-// init() ...
|
|
|
-// mint_treasury()...
|
|
|
-// airdrop() ...
|
|
|
-// }
|
|
|
-//
|
|
|
-// pub struct Dao {
|
|
|
-// dao_params: DaoParams,
|
|
|
-// dao_wallet: DaoWallet,
|
|
|
-// }
|
|
|
-//
|
|
|
-// pub struct Client {
|
|
|
-// dao: Dao,
|
|
|
-// money_wallet: MoneyWallet,
|
|
|
-// }
|
|
|
-//
|
|
|
-// fn start() {
|
|
|
-// cashier::init();
|
|
|
-//
|
|
|
-// match input {
|
|
|
-// dao_create() => Dao::new()
|
|
|
-// wallet_create() => Client::new(money_wallet::new(), dao)
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
pub struct Client {
|
|
|
dao_wallet: DaoWallet,
|
|
|
- money_wallet: MoneyWallet,
|
|
|
- states: StateRegistry,
|
|
|
- zk_bins: ZkContractTable,
|
|
|
+ money_wallets: HashMap<String, MoneyWallet>,
|
|
|
}
|
|
|
|
|
|
impl Client {
|
|
|
+ fn new() -> Self {
|
|
|
+ let dao_wallet = DaoWallet::new();
|
|
|
+
|
|
|
+ let money_wallets = HashMap::default();
|
|
|
+
|
|
|
+ Self { dao_wallet, money_wallets }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn new_money_wallet(&mut self, key: String) {
|
|
|
+ let keypair = Keypair::random(&mut OsRng);
|
|
|
+ let signature_secret = SecretKey::random(&mut OsRng);
|
|
|
+ let leaf_position = Position::zero();
|
|
|
+ let money_wallet = MoneyWallet { keypair, signature_secret, leaf_position };
|
|
|
+ self.money_wallets.insert(key, money_wallet);
|
|
|
+ }
|
|
|
+
|
|
|
// TODO: user passes DAO approval ratio: 1/2
|
|
|
// we parse that into dao_approval_ratio_base and dao_approval_ratio_quot
|
|
|
fn create_dao(
|
|
|
@@ -93,156 +72,62 @@ impl Client {
|
|
|
dao_approval_ratio_quot: u64,
|
|
|
dao_approval_ratio_base: u64,
|
|
|
token_id: pallas::Base,
|
|
|
- ) -> pallas::Base {
|
|
|
- let tx = self.dao_wallet.build_mint_tx(
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
+ states: &mut StateRegistry,
|
|
|
+ ) -> Result<pallas::Base> {
|
|
|
+ let tx = self.dao_wallet.mint_tx(
|
|
|
dao_proposer_limit,
|
|
|
dao_quorum,
|
|
|
dao_approval_ratio_quot,
|
|
|
dao_approval_ratio_base,
|
|
|
token_id,
|
|
|
- &self.zk_bins,
|
|
|
+ zk_bins,
|
|
|
);
|
|
|
|
|
|
- self.validate(&tx);
|
|
|
-
|
|
|
- self.dao_wallet.balances(&mut self.states);
|
|
|
+ // TODO: Proper error handling.
|
|
|
+ // Only witness the value once the transaction is confirmed.
|
|
|
+ match self.validate(&tx, states, zk_bins) {
|
|
|
+ Ok(v) => self.dao_wallet.update_witness(states)?,
|
|
|
+ Err(e) => {}
|
|
|
+ }
|
|
|
|
|
|
+ // Retrieve DAO bulla from the state.
|
|
|
let dao_bulla = {
|
|
|
- assert_eq!(tx.func_calls.len(), 1);
|
|
|
let func_call = &tx.func_calls[0];
|
|
|
let call_data = func_call.call_data.as_any();
|
|
|
- assert_eq!(
|
|
|
- (&*call_data).type_id(),
|
|
|
- TypeId::of::<dao_contract::mint::validate::CallData>()
|
|
|
- );
|
|
|
let call_data =
|
|
|
call_data.downcast_ref::<dao_contract::mint::validate::CallData>().unwrap();
|
|
|
call_data.dao_bulla.clone()
|
|
|
};
|
|
|
|
|
|
+ // TODO: instead of this print statement, return DAO bulla to CLI
|
|
|
debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
|
|
|
|
|
|
- dao_bulla.0
|
|
|
- }
|
|
|
-
|
|
|
- fn init(&mut self) -> Result<()> {
|
|
|
- debug!(target: "demo", "Loading dao-mint.zk");
|
|
|
- let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
|
|
|
- let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
|
|
|
- self.zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
|
|
|
-
|
|
|
- debug!(target: "demo", "Loading money-transfer contracts");
|
|
|
- let start = Instant::now();
|
|
|
- let mint_pk = ProvingKey::build(11, &MintContract::default());
|
|
|
- debug!("Mint PK: [{:?}]", start.elapsed());
|
|
|
- let start = Instant::now();
|
|
|
- let burn_pk = ProvingKey::build(11, &BurnContract::default());
|
|
|
- debug!("Burn PK: [{:?}]", start.elapsed());
|
|
|
- let start = Instant::now();
|
|
|
- let mint_vk = VerifyingKey::build(11, &MintContract::default());
|
|
|
- debug!("Mint VK: [{:?}]", start.elapsed());
|
|
|
- let start = Instant::now();
|
|
|
- let burn_vk = VerifyingKey::build(11, &BurnContract::default());
|
|
|
- debug!("Burn VK: [{:?}]", start.elapsed());
|
|
|
-
|
|
|
- self.zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
|
|
|
- self.zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
|
|
|
- debug!(target: "demo", "Loading dao-propose-main.zk");
|
|
|
- let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
|
|
|
- let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
|
|
|
- self.zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
|
|
|
- debug!(target: "demo", "Loading dao-propose-burn.zk");
|
|
|
- let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
|
|
|
- let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
|
|
|
- self.zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
|
|
|
- debug!(target: "demo", "Loading dao-vote-main.zk");
|
|
|
- let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
|
|
|
- let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
|
|
|
- self.zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
|
|
|
- debug!(target: "demo", "Loading dao-vote-burn.zk");
|
|
|
- let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
|
|
|
- let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
|
|
|
- self.zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
|
|
|
- let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
|
|
|
- let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
|
|
|
- self.zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
|
|
|
-
|
|
|
- // State for money contracts
|
|
|
- // TODO: we need the cashier value elsewhere.
|
|
|
- let cashier_signature_secret = SecretKey::random(&mut OsRng);
|
|
|
- let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
|
|
|
- let faucet_signature_secret = SecretKey::random(&mut OsRng);
|
|
|
- let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
|
|
|
+ // We create a hashmap so we can easily retrieve DAO values for the demo.
|
|
|
+ let dao_params = DaoParams {
|
|
|
+ proposer_limit: dao_proposer_limit,
|
|
|
+ quorum: dao_quorum,
|
|
|
+ approval_ratio_quot: dao_approval_ratio_quot,
|
|
|
+ approval_ratio_base: dao_approval_ratio_base,
|
|
|
+ gov_token_id: token_id,
|
|
|
+ public_key: self.dao_wallet.keypair.public,
|
|
|
+ bulla_blind: self.dao_wallet.bulla_blind,
|
|
|
+ };
|
|
|
|
|
|
- ///////////////////////////////////////////////////
|
|
|
- let money_state =
|
|
|
- money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
|
|
|
- self.states.register(*money_contract::CONTRACT_ID, money_state);
|
|
|
- /////////////////////////////////////////////////////
|
|
|
- let dao_state = dao_contract::State::new();
|
|
|
- self.states.register(*dao_contract::CONTRACT_ID, dao_state);
|
|
|
- /////////////////////////////////////////////////////
|
|
|
+ self.dao_wallet.params.insert(HashableBase(dao_bulla.0), dao_params);
|
|
|
|
|
|
- Ok(())
|
|
|
+ Ok(dao_bulla.0)
|
|
|
}
|
|
|
|
|
|
- // TODO: user passes "gDRK", we match with gDRK tokenID
|
|
|
- fn mint_treasury(
|
|
|
+ // TODO: Change these into errors instead of expects.
|
|
|
+ fn validate(
|
|
|
&mut self,
|
|
|
- token_id: pallas::Base,
|
|
|
- token_supply: u64,
|
|
|
- dao_bulla: pallas::Base,
|
|
|
- recipient: PublicKey,
|
|
|
+ tx: &Transaction,
|
|
|
+ states: &mut StateRegistry,
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
) -> Result<()> {
|
|
|
- let spend_hook = *dao_contract::exec::FUNC_ID;
|
|
|
-
|
|
|
- let user_data = dao_bulla;
|
|
|
- let value = token_supply;
|
|
|
- let tx = self.money_wallet.build_transfer_tx(
|
|
|
- value,
|
|
|
- token_id,
|
|
|
- spend_hook,
|
|
|
- user_data,
|
|
|
- recipient,
|
|
|
- &self.zk_bins,
|
|
|
- )?;
|
|
|
-
|
|
|
- self.validate(&tx);
|
|
|
-
|
|
|
- let own_coin = self.dao_wallet.balances(&mut self.states)?;
|
|
|
-
|
|
|
- // TODO: return own_coin.note.value to CLI
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- fn airdrop(&mut self, value: u64, token_id: pallas::Base, recipient: PublicKey) -> Result<()> {
|
|
|
- // Spend hook and user data disabled
|
|
|
- let spend_hook = DrkSpendHook::from(0);
|
|
|
- let user_data = DrkUserData::from(0);
|
|
|
-
|
|
|
- let tx = self.money_wallet.build_transfer_tx(
|
|
|
- value,
|
|
|
- token_id,
|
|
|
- spend_hook,
|
|
|
- user_data,
|
|
|
- recipient,
|
|
|
- &self.zk_bins,
|
|
|
- )?;
|
|
|
-
|
|
|
- self.validate(&tx);
|
|
|
-
|
|
|
- let own_coin = self.money_wallet.balances(&mut self.states)?;
|
|
|
-
|
|
|
- // TODO: return own_coin.note.value to CLI
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- fn validate(&mut self, tx: &Transaction) -> Result<()> {
|
|
|
let mut updates = vec![];
|
|
|
|
|
|
- let states = &self.states;
|
|
|
// Validate all function calls in the tx
|
|
|
for (idx, func_call) in tx.func_calls.iter().enumerate() {
|
|
|
// So then the verifier will lookup the corresponding state_transition and apply
|
|
|
@@ -278,27 +163,33 @@ impl Client {
|
|
|
|
|
|
// Atomically apply all changes
|
|
|
for update in updates {
|
|
|
- update.apply(&mut self.states);
|
|
|
+ update.apply(states);
|
|
|
}
|
|
|
|
|
|
- tx.zk_verify(&self.zk_bins);
|
|
|
+ tx.zk_verify(zk_bins);
|
|
|
tx.verify_sigs();
|
|
|
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+ // TODO: error handling
|
|
|
fn propose(
|
|
|
&mut self,
|
|
|
params: DaoParams,
|
|
|
recipient: PublicKey,
|
|
|
token_id: pallas::Base,
|
|
|
amount: u64,
|
|
|
+ key: String,
|
|
|
+ states: &mut StateRegistry,
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
) -> Result<()> {
|
|
|
- let dao_leaf_position = self.dao_wallet.witness(&mut self.states)?;
|
|
|
+ let dao_leaf_position = self.dao_wallet.leaf_position;
|
|
|
+
|
|
|
+ let mut money_wallet = self.money_wallets.get_mut(&key).unwrap();
|
|
|
|
|
|
- let tx = self.money_wallet.build_propose_tx(
|
|
|
- &mut self.states,
|
|
|
- &self.zk_bins,
|
|
|
+ let tx = money_wallet.propose_tx(
|
|
|
+ states,
|
|
|
+ &zk_bins,
|
|
|
params,
|
|
|
recipient,
|
|
|
token_id,
|
|
|
@@ -306,65 +197,110 @@ impl Client {
|
|
|
dao_leaf_position,
|
|
|
)?;
|
|
|
|
|
|
- self.validate(&tx)?;
|
|
|
+ self.validate(&tx, states, zk_bins)?;
|
|
|
|
|
|
self.dao_wallet.read_proposal(&tx)?;
|
|
|
|
|
|
Ok(())
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- // TODO: User must have the values Proposal and DaoParams in order to cast a vote.
|
|
|
- // These should be encoded to base58 and printed to command-line when a DAO is made (DaoParams)
|
|
|
- // and a Proposal is made (Proposal). Then the user loads a base58 string into the vote request.
|
|
|
- fn vote(&mut self, vote_option: bool, proposal: Proposal, dao_params: DaoParams) -> Result<()> {
|
|
|
- let dao_keypair = self.dao_wallet.get_vote_decryption_key();
|
|
|
-
|
|
|
- let tx = self.money_wallet.build_vote_tx(
|
|
|
- vote_option,
|
|
|
- &mut self.states,
|
|
|
- &self.zk_bins,
|
|
|
- dao_keypair,
|
|
|
- proposal,
|
|
|
- dao_params,
|
|
|
- )?;
|
|
|
+struct DaoWallet {
|
|
|
+ keypair: Keypair,
|
|
|
+ signature_secret: SecretKey,
|
|
|
+ bulla_blind: pallas::Base,
|
|
|
+ leaf_position: Position,
|
|
|
+ params: HashMap<HashableBase, DaoParams>,
|
|
|
+ vote_notes: Vec<dao_contract::vote::wallet::Note>,
|
|
|
+}
|
|
|
+impl DaoWallet {
|
|
|
+ fn new() -> Self {
|
|
|
+ let keypair = Keypair::random(&mut OsRng);
|
|
|
+ let signature_secret = SecretKey::random(&mut OsRng);
|
|
|
+ let bulla_blind = pallas::Base::random(&mut OsRng);
|
|
|
+ let leaf_position = Position::zero();
|
|
|
+ let params: HashMap<HashableBase, DaoParams> = HashMap::default();
|
|
|
+ let vote_notes = Vec::new();
|
|
|
|
|
|
- self.validate(&tx)?;
|
|
|
+ Self { keypair, signature_secret, bulla_blind, leaf_position, params, vote_notes }
|
|
|
+ }
|
|
|
|
|
|
- self.dao_wallet.read_vote(&tx)?;
|
|
|
+ // Mint the DAO bulla.
|
|
|
+ fn mint_tx(
|
|
|
+ &mut self,
|
|
|
+ dao_proposer_limit: u64,
|
|
|
+ dao_quorum: u64,
|
|
|
+ dao_approval_ratio_quot: u64,
|
|
|
+ dao_approval_ratio_base: u64,
|
|
|
+ token_id: pallas::Base,
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
+ ) -> Transaction {
|
|
|
+ let builder = dao_contract::mint::wallet::Builder {
|
|
|
+ dao_proposer_limit,
|
|
|
+ dao_quorum,
|
|
|
+ dao_approval_ratio_quot,
|
|
|
+ dao_approval_ratio_base,
|
|
|
+ gov_token_id: *GDRK_ID,
|
|
|
+ dao_pubkey: self.keypair.public,
|
|
|
+ dao_bulla_blind: self.bulla_blind,
|
|
|
+ _signature_secret: self.signature_secret,
|
|
|
+ };
|
|
|
+ let func_call = builder.build(zk_bins);
|
|
|
+ let func_calls = vec![func_call];
|
|
|
|
|
|
- Ok(())
|
|
|
+ let signatures = sign(vec![self.signature_secret], &func_calls);
|
|
|
+ Transaction { func_calls, signatures }
|
|
|
}
|
|
|
|
|
|
- // TODO: user must pass in a base58 encoded string of the Proposal, proposal_bulla and
|
|
|
- // DaoParams
|
|
|
- fn exec(
|
|
|
- &mut self,
|
|
|
- proposal: Proposal,
|
|
|
- proposal_bulla: pallas::Base,
|
|
|
- dao_params: DaoParams,
|
|
|
- ) -> Result<()> {
|
|
|
- self.dao_wallet.build_exec_tx(
|
|
|
- &mut self.states,
|
|
|
- &self.zk_bins,
|
|
|
- proposal,
|
|
|
- proposal_bulla,
|
|
|
- dao_params,
|
|
|
- )?;
|
|
|
+ // TODO: error handling
|
|
|
+ fn update_witness(&mut self, states: &mut StateRegistry) -> Result<()> {
|
|
|
+ let state = states.lookup_mut::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
|
|
|
+ let path = state.dao_tree.witness();
|
|
|
+ match path {
|
|
|
+ Some(path) => {
|
|
|
+ self.leaf_position = path;
|
|
|
+ }
|
|
|
+ None => {}
|
|
|
+ }
|
|
|
Ok(())
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-// DAO private values. This class is purely concerned with the DAO treasury.
|
|
|
-// TODO: we must call track() for keypairs before we can query the balance.
|
|
|
-pub struct DaoWallet {
|
|
|
- keypair: Keypair,
|
|
|
- vote_keypair: Keypair,
|
|
|
- signature: SecretKey,
|
|
|
- params: DaoParams,
|
|
|
- vote_notes: Vec<dao_contract::vote::wallet::Note>,
|
|
|
-}
|
|
|
+ fn balances(&self, states: &mut StateRegistry) -> Result<OwnCoin> {
|
|
|
+ let state =
|
|
|
+ states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
|
|
|
+
|
|
|
+ let mut recv_coins = state.wallet_cache.get_received(&self.keypair.secret);
|
|
|
+
|
|
|
+ let dao_recv_coin = recv_coins.pop().unwrap();
|
|
|
+ let treasury_note = dao_recv_coin.note.clone();
|
|
|
+
|
|
|
+ let coords = self.keypair.public.0.to_affine().coordinates().unwrap();
|
|
|
+ let coin = poseidon_hash::<8>([
|
|
|
+ *coords.x(),
|
|
|
+ *coords.y(),
|
|
|
+ DrkValue::from(treasury_note.value),
|
|
|
+ treasury_note.token_id,
|
|
|
+ treasury_note.serial,
|
|
|
+ treasury_note.spend_hook,
|
|
|
+ treasury_note.user_data,
|
|
|
+ treasury_note.coin_blind,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // TODO: Error handling
|
|
|
+ if coin == dao_recv_coin.coin.0 {
|
|
|
+ // return Ok(dao_recv_coin)
|
|
|
+ }
|
|
|
+ // else {
|
|
|
+ // return Err::InvalidCoin
|
|
|
+ // }
|
|
|
+
|
|
|
+ // TODO: this is the CLI output.
|
|
|
+ debug!("DAO received a coin worth {} xDRK", treasury_note.value);
|
|
|
+
|
|
|
+ // TODO: just return the value of the coin, not OwnCoin.
|
|
|
+ Ok(dao_recv_coin)
|
|
|
+ }
|
|
|
|
|
|
-impl DaoWallet {
|
|
|
fn read_proposal(&self, tx: &Transaction) -> Result<()> {
|
|
|
let (proposal, proposal_bulla) = {
|
|
|
let func_call = &tx.func_calls[0];
|
|
|
@@ -390,7 +326,6 @@ impl DaoWallet {
|
|
|
|
|
|
// TODO: encode Proposal as base58 and return to cli
|
|
|
}
|
|
|
-
|
|
|
// We decrypt the votes in a transaction and add it to the wallet.
|
|
|
fn read_vote(&mut self, tx: &Transaction) -> Result<()> {
|
|
|
let vote_note = {
|
|
|
@@ -401,7 +336,7 @@ impl DaoWallet {
|
|
|
|
|
|
let header = &call_data.header;
|
|
|
let note: dao_contract::vote::wallet::Note =
|
|
|
- header.enc_note.decrypt(&self.vote_keypair.secret).unwrap();
|
|
|
+ header.enc_note.decrypt(&self.keypair.secret).unwrap();
|
|
|
note
|
|
|
};
|
|
|
|
|
|
@@ -416,84 +351,8 @@ impl DaoWallet {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
- // We need to encrypt votes to the DAO secret key for this demo, which requires users
|
|
|
- // to have access to a secret key operated by the DAO. We create a specific key for decrypting
|
|
|
- // votes which is different to the key that operates the treasury.
|
|
|
- fn get_vote_decryption_key(&self) -> Keypair {
|
|
|
- self.vote_keypair
|
|
|
- }
|
|
|
-
|
|
|
- fn track(&self, states: &mut StateRegistry) -> Result<()> {
|
|
|
- let state =
|
|
|
- states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
|
|
|
- state.wallet_cache.track(self.keypair.secret);
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- fn witness(&self, states: &mut StateRegistry) -> Result<Position> {
|
|
|
- let state = states.lookup_mut::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
|
|
|
- let path = state.dao_tree.witness();
|
|
|
- // TODO: error handling
|
|
|
- //if path.is_some() {
|
|
|
- return Ok(path.unwrap())
|
|
|
- //}
|
|
|
- }
|
|
|
-
|
|
|
- fn balances(&self, states: &mut StateRegistry) -> Result<OwnCoin> {
|
|
|
- let state =
|
|
|
- states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
|
|
|
-
|
|
|
- let mut recv_coins = state.wallet_cache.get_received(&self.keypair.secret);
|
|
|
-
|
|
|
- let dao_recv_coin = recv_coins.pop().unwrap();
|
|
|
- let treasury_note = dao_recv_coin.note.clone();
|
|
|
-
|
|
|
- debug!("DAO received a coin worth {} xDRK", treasury_note.value);
|
|
|
-
|
|
|
- Ok(dao_recv_coin)
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: encode this to base58 and display in cli
|
|
|
- fn params(&self) -> Result<&DaoParams> {
|
|
|
- Ok(&self.params)
|
|
|
- }
|
|
|
-
|
|
|
- fn build_mint_tx(
|
|
|
- &self,
|
|
|
- dao_proposer_limit: u64,
|
|
|
- dao_quorum: u64,
|
|
|
- dao_approval_ratio_quot: u64,
|
|
|
- dao_approval_ratio_base: u64,
|
|
|
- token_id: pallas::Base,
|
|
|
- zk_bins: &ZkContractTable,
|
|
|
- ) -> Transaction {
|
|
|
- // TODO: store this?
|
|
|
- let dao_bulla_blind = pallas::Base::random(&mut OsRng);
|
|
|
-
|
|
|
- let builder = dao_contract::mint::wallet::Builder {
|
|
|
- dao_proposer_limit,
|
|
|
- dao_quorum,
|
|
|
- dao_approval_ratio_quot,
|
|
|
- dao_approval_ratio_base,
|
|
|
- gov_token_id: *GDRK_ID,
|
|
|
- dao_pubkey: self.keypair.public,
|
|
|
- dao_bulla_blind,
|
|
|
- _signature_secret: self.signature,
|
|
|
- };
|
|
|
- let func_call = builder.build(zk_bins);
|
|
|
- let func_calls = vec![func_call];
|
|
|
-
|
|
|
- // TODO: this should be a cashier key?
|
|
|
- let signatures = sign(vec![self.signature], &func_calls);
|
|
|
- let tx = Transaction { func_calls, signatures };
|
|
|
- tx
|
|
|
- }
|
|
|
-
|
|
|
- // We use this to prove ownership of treasury tokens.
|
|
|
- // Right now this method is duplicated on both wallets but doesn't need to be.
|
|
|
- // TODO: clean up the architecture.
|
|
|
- fn coin_path(
|
|
|
+ // TODO: Explicit error handling.
|
|
|
+ fn get_treasury_path(
|
|
|
&self,
|
|
|
states: &StateRegistry,
|
|
|
own_coin: &OwnCoin,
|
|
|
@@ -519,13 +378,20 @@ impl DaoWallet {
|
|
|
proposal_bulla: pallas::Base,
|
|
|
dao_params: DaoParams,
|
|
|
) -> Result<Transaction> {
|
|
|
+ // TODO: move these to DAO struct?
|
|
|
+ let tx_signature_secret = SecretKey::random(&mut OsRng);
|
|
|
+ let exec_signature_secret = SecretKey::random(&mut OsRng);
|
|
|
+
|
|
|
+ // We must prove we have sufficient governance tokens to execute this.
|
|
|
let own_coin = self.balances(states)?;
|
|
|
|
|
|
- let (treasury_leaf_position, treasury_merkle_path) = self.coin_path(states, &own_coin)?;
|
|
|
+ let (treasury_leaf_position, treasury_merkle_path) =
|
|
|
+ self.get_treasury_path(states, &own_coin)?;
|
|
|
|
|
|
let input_value = own_coin.note.value;
|
|
|
|
|
|
// TODO: not sure what this is doing
|
|
|
+ // Should this be moved into a different struct?
|
|
|
let user_serial = pallas::Base::random(&mut OsRng);
|
|
|
let user_coin_blind = pallas::Base::random(&mut OsRng);
|
|
|
let user_data_blind = pallas::Base::random(&mut OsRng);
|
|
|
@@ -542,7 +408,7 @@ impl DaoWallet {
|
|
|
user_data_blind,
|
|
|
value_blind: input_value_blind,
|
|
|
// TODO: in schema, we create random signatures here. why?
|
|
|
- signature_secret: self.signature,
|
|
|
+ signature_secret: tx_signature_secret,
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -609,31 +475,29 @@ impl DaoWallet {
|
|
|
input_value: proposal.amount,
|
|
|
input_value_blind,
|
|
|
hook_dao_exec: *dao_contract::exec::FUNC_ID,
|
|
|
- signature_secret: self.signature,
|
|
|
+ signature_secret: exec_signature_secret,
|
|
|
}
|
|
|
};
|
|
|
|
|
|
let exec_func_call = builder.build(zk_bins);
|
|
|
let func_calls = vec![transfer_func_call, exec_func_call];
|
|
|
|
|
|
- // TODO: we sign both transactions with the same sig, is this wrong?
|
|
|
- let signatures = sign(vec![self.signature, self.signature], &func_calls);
|
|
|
+ let signatures = sign(vec![tx_signature_secret, exec_signature_secret], &func_calls);
|
|
|
Ok(Transaction { func_calls, signatures })
|
|
|
}
|
|
|
}
|
|
|
-// Money private values.
|
|
|
-pub struct MoneyWallet {
|
|
|
+
|
|
|
+// Stores governance tokens and related secret values.
|
|
|
+#[derive(Clone)]
|
|
|
+struct MoneyWallet {
|
|
|
keypair: Keypair,
|
|
|
- signature: SecretKey,
|
|
|
+ signature_secret: SecretKey,
|
|
|
+ leaf_position: Position,
|
|
|
}
|
|
|
|
|
|
impl MoneyWallet {
|
|
|
- fn track(&self, states: &mut StateRegistry) -> Result<()> {
|
|
|
- let state =
|
|
|
- states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
|
|
|
- state.wallet_cache.track(self.keypair.secret);
|
|
|
-
|
|
|
- Ok(())
|
|
|
+ fn signature_public(&self) -> PublicKey {
|
|
|
+ PublicKey::from_secret(self.signature_secret)
|
|
|
}
|
|
|
|
|
|
fn balances(&self, states: &mut StateRegistry) -> Result<OwnCoin> {
|
|
|
@@ -645,66 +509,14 @@ impl MoneyWallet {
|
|
|
let recv_coin = recv_coins.pop().unwrap();
|
|
|
let note = recv_coin.note.clone();
|
|
|
|
|
|
+ // TODO: this should output to command line
|
|
|
debug!("User received a coin worth {} gDRK", note.value);
|
|
|
|
|
|
+ // TODO: don't return the coin, just return the value
|
|
|
Ok(recv_coin)
|
|
|
}
|
|
|
|
|
|
- // We use this to prove ownership of governance tokens.
|
|
|
- fn coin_path(
|
|
|
- &self,
|
|
|
- states: &StateRegistry,
|
|
|
- own_coin: &OwnCoin,
|
|
|
- ) -> Result<(Position, Vec<MerkleNode>)> {
|
|
|
- let (money_leaf_position, money_merkle_path) = {
|
|
|
- let state =
|
|
|
- states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
|
|
|
- let tree = &state.tree;
|
|
|
- let leaf_position = own_coin.leaf_position.clone();
|
|
|
- let root = tree.root(0).unwrap();
|
|
|
- let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
|
|
|
- (leaf_position, merkle_path)
|
|
|
- };
|
|
|
-
|
|
|
- Ok((money_leaf_position, money_merkle_path))
|
|
|
- }
|
|
|
-
|
|
|
- fn build_transfer_tx(
|
|
|
- &self,
|
|
|
- value: u64,
|
|
|
- token_id: pallas::Base,
|
|
|
- spend_hook: pallas::Base,
|
|
|
- user_data: pallas::Base,
|
|
|
- recipient: PublicKey,
|
|
|
- zk_bins: &ZkContractTable,
|
|
|
- ) -> Result<Transaction> {
|
|
|
- let builder = {
|
|
|
- money_contract::transfer::wallet::Builder {
|
|
|
- clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
|
|
|
- value,
|
|
|
- token_id,
|
|
|
- signature_secret: self.signature,
|
|
|
- }],
|
|
|
- inputs: vec![],
|
|
|
- outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
|
|
|
- value,
|
|
|
- token_id,
|
|
|
- public: recipient,
|
|
|
- serial: pallas::Base::random(&mut OsRng),
|
|
|
- coin_blind: pallas::Base::random(&mut OsRng),
|
|
|
- spend_hook,
|
|
|
- user_data,
|
|
|
- }],
|
|
|
- }
|
|
|
- };
|
|
|
- let func_call = builder.build(zk_bins)?;
|
|
|
- let func_calls = vec![func_call];
|
|
|
-
|
|
|
- let signatures = sign(vec![self.signature], &func_calls);
|
|
|
- Ok(Transaction { func_calls, signatures })
|
|
|
- }
|
|
|
-
|
|
|
- fn build_propose_tx(
|
|
|
+ fn propose_tx(
|
|
|
&mut self,
|
|
|
states: &mut StateRegistry,
|
|
|
zk_bins: &ZkContractTable,
|
|
|
@@ -714,9 +526,11 @@ impl MoneyWallet {
|
|
|
amount: u64,
|
|
|
dao_leaf_position: Position,
|
|
|
) -> Result<Transaction> {
|
|
|
+ // To be able to make a proposal, we must prove we have ownership of governance tokens,
|
|
|
+ // and that the quantity of governance tokens is within the accepted proposal limit.
|
|
|
let own_coin = self.balances(states)?;
|
|
|
|
|
|
- let (money_leaf_position, money_merkle_path) = self.coin_path(&states, &own_coin)?;
|
|
|
+ let (money_leaf_position, money_merkle_path) = self.get_path(&states, &own_coin)?;
|
|
|
|
|
|
let signature_secret = SecretKey::random(&mut OsRng);
|
|
|
|
|
|
@@ -764,7 +578,29 @@ impl MoneyWallet {
|
|
|
Ok(Transaction { func_calls, signatures })
|
|
|
}
|
|
|
|
|
|
- fn build_vote_tx(
|
|
|
+ // TODO: Explicit error handling.
|
|
|
+ fn get_path(
|
|
|
+ &self,
|
|
|
+ states: &StateRegistry,
|
|
|
+ own_coin: &OwnCoin,
|
|
|
+ ) -> Result<(Position, Vec<MerkleNode>)> {
|
|
|
+ let (money_leaf_position, money_merkle_path) = {
|
|
|
+ let state =
|
|
|
+ states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
|
|
|
+ let tree = &state.tree;
|
|
|
+ let leaf_position = own_coin.leaf_position.clone();
|
|
|
+ let root = tree.root(0).unwrap();
|
|
|
+ let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
|
|
|
+ (leaf_position, merkle_path)
|
|
|
+ };
|
|
|
+
|
|
|
+ Ok((money_leaf_position, money_merkle_path))
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: User must have the values Proposal and DaoParams in order to cast a vote.
|
|
|
+ // These should be encoded to base58 and printed to command-line when a DAO is made (DaoParams)
|
|
|
+ // and a Proposal is made (Proposal). Then the user loads a base58 string into the vote request.
|
|
|
+ fn vote_tx(
|
|
|
&mut self,
|
|
|
vote_option: bool,
|
|
|
states: &mut StateRegistry,
|
|
|
@@ -773,9 +609,10 @@ impl MoneyWallet {
|
|
|
proposal: Proposal,
|
|
|
dao_params: DaoParams,
|
|
|
) -> Result<Transaction> {
|
|
|
+ // We must prove we have governance tokens in order to vote.
|
|
|
let own_coin = self.balances(states)?;
|
|
|
|
|
|
- let (money_leaf_position, money_merkle_path) = self.coin_path(states, &own_coin)?;
|
|
|
+ let (money_leaf_position, money_merkle_path) = self.get_path(states, &own_coin)?;
|
|
|
|
|
|
let input = {
|
|
|
dao_contract::vote::wallet::BuilderInput {
|
|
|
@@ -783,7 +620,7 @@ impl MoneyWallet {
|
|
|
note: own_coin.note.clone(),
|
|
|
leaf_position: money_leaf_position,
|
|
|
merkle_path: money_merkle_path,
|
|
|
- signature_secret: self.signature,
|
|
|
+ signature_secret: self.signature_secret,
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -802,62 +639,187 @@ impl MoneyWallet {
|
|
|
let func_call = builder.build(zk_bins);
|
|
|
let func_calls = vec![func_call];
|
|
|
|
|
|
- let signatures = sign(vec![self.signature], &func_calls);
|
|
|
+ let signatures = sign(vec![self.signature_secret], &func_calls);
|
|
|
Ok(Transaction { func_calls, signatures })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-pub struct DaoDemo {}
|
|
|
+async fn start_rpc(demo: Demo) -> Result<()> {
|
|
|
+ let rpc_addr = Url::parse("tcp://127.0.0.1:7777")?;
|
|
|
+
|
|
|
+ let client = JsonRpcInterface::new(demo);
|
|
|
+
|
|
|
+ let rpc_interface = Arc::new(client);
|
|
|
+
|
|
|
+ listen_and_serve(rpc_addr, rpc_interface).await?;
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+pub struct Demo {
|
|
|
+ cashier: Cashier,
|
|
|
+ client: Client,
|
|
|
+ states: StateRegistry,
|
|
|
+ zk_bins: ZkContractTable,
|
|
|
+}
|
|
|
+
|
|
|
+impl Demo {
|
|
|
+ fn new() -> Self {
|
|
|
+ let cashier = Cashier::new();
|
|
|
+ let client = Client::new();
|
|
|
+
|
|
|
+ // Lookup table for smart contract states
|
|
|
+ let mut states = StateRegistry::new();
|
|
|
+
|
|
|
+ // Initialize ZK binary table
|
|
|
+ let mut zk_bins = ZkContractTable::new();
|
|
|
|
|
|
-impl DaoDemo {
|
|
|
- pub fn new() -> Self {
|
|
|
- Self {}
|
|
|
+ Self { cashier, client, states, zk_bins }
|
|
|
}
|
|
|
|
|
|
fn init(&mut self) -> Result<()> {
|
|
|
- Ok(())
|
|
|
- }
|
|
|
+ // We use these to initialize the money state.
|
|
|
+ let faucet_signature_secret = SecretKey::random(&mut OsRng);
|
|
|
+ let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
|
|
|
|
|
|
- fn create(&mut self) -> Result<()> {
|
|
|
- Ok(())
|
|
|
- }
|
|
|
+ debug!(target: "demo", "Loading dao-mint.zk");
|
|
|
+ let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
|
|
|
+ let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
|
|
|
+ self.zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
|
|
|
+
|
|
|
+ debug!(target: "demo", "Loading money-transfer contracts");
|
|
|
+ let start = Instant::now();
|
|
|
+ let mint_pk = ProvingKey::build(11, &MintContract::default());
|
|
|
+ debug!("Mint PK: [{:?}]", start.elapsed());
|
|
|
+ let start = Instant::now();
|
|
|
+ let burn_pk = ProvingKey::build(11, &BurnContract::default());
|
|
|
+ debug!("Burn PK: [{:?}]", start.elapsed());
|
|
|
+ let start = Instant::now();
|
|
|
+ let mint_vk = VerifyingKey::build(11, &MintContract::default());
|
|
|
+ debug!("Mint VK: [{:?}]", start.elapsed());
|
|
|
+ let start = Instant::now();
|
|
|
+ let burn_vk = VerifyingKey::build(11, &BurnContract::default());
|
|
|
+ debug!("Burn VK: [{:?}]", start.elapsed());
|
|
|
+
|
|
|
+ self.zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
|
|
|
+ self.zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
|
|
|
+ debug!(target: "demo", "Loading dao-propose-main.zk");
|
|
|
+ let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
|
|
|
+ let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
|
|
|
+ self.zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
|
|
|
+ debug!(target: "demo", "Loading dao-propose-burn.zk");
|
|
|
+ let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
|
|
|
+ let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
|
|
|
+ self.zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
|
|
|
+ debug!(target: "demo", "Loading dao-vote-main.zk");
|
|
|
+ let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
|
|
|
+ let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
|
|
|
+ self.zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
|
|
|
+ debug!(target: "demo", "Loading dao-vote-burn.zk");
|
|
|
+ let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
|
|
|
+ let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
|
|
|
+ self.zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
|
|
|
+ let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
|
|
|
+ let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
|
|
|
+ self.zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
|
|
|
+
|
|
|
+ let cashier_signature_public = self.cashier.signature_public();
|
|
|
+
|
|
|
+ let money_state =
|
|
|
+ money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
|
|
|
+ self.states.register(*money_contract::CONTRACT_ID, money_state);
|
|
|
+
|
|
|
+ let dao_state = dao_contract::State::new();
|
|
|
+ self.states.register(*dao_contract::CONTRACT_ID, dao_state);
|
|
|
|
|
|
- fn mint(&mut self) -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- fn airdrop(&mut self) -> Result<()> {
|
|
|
- Ok(())
|
|
|
+// Mint authority that mints the DAO treasury and airdrops governance tokens.
|
|
|
+#[derive(Clone)]
|
|
|
+struct Cashier {
|
|
|
+ keypair: Keypair,
|
|
|
+ signature_secret: SecretKey,
|
|
|
+}
|
|
|
+
|
|
|
+impl Cashier {
|
|
|
+ fn new() -> Self {
|
|
|
+ let keypair = Keypair::random(&mut OsRng);
|
|
|
+ let signature_secret = SecretKey::random(&mut OsRng);
|
|
|
+ Self { keypair, signature_secret }
|
|
|
}
|
|
|
|
|
|
- fn propose(&mut self) -> Result<()> {
|
|
|
- Ok(())
|
|
|
+ fn signature_public(&self) -> PublicKey {
|
|
|
+ PublicKey::from_secret(self.signature_secret)
|
|
|
}
|
|
|
|
|
|
- fn vote(&mut self) -> Result<()> {
|
|
|
- Ok(())
|
|
|
+ fn mint_treasury(
|
|
|
+ &mut self,
|
|
|
+ token_id: pallas::Base,
|
|
|
+ token_supply: u64,
|
|
|
+ dao_bulla: pallas::Base,
|
|
|
+ recipient: PublicKey,
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
+ ) -> Result<Transaction> {
|
|
|
+ let spend_hook = *dao_contract::exec::FUNC_ID;
|
|
|
+ let user_data = dao_bulla;
|
|
|
+ let value = token_supply;
|
|
|
+
|
|
|
+ let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
|
|
|
+
|
|
|
+ Ok(tx)
|
|
|
}
|
|
|
|
|
|
- fn exec(&mut self) -> Result<()> {
|
|
|
- Ok(())
|
|
|
+ fn transfer_tx(
|
|
|
+ &self,
|
|
|
+ value: u64,
|
|
|
+ token_id: pallas::Base,
|
|
|
+ spend_hook: pallas::Base,
|
|
|
+ user_data: pallas::Base,
|
|
|
+ recipient: PublicKey,
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
+ ) -> Result<Transaction> {
|
|
|
+ let builder = {
|
|
|
+ money_contract::transfer::wallet::Builder {
|
|
|
+ clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
|
|
|
+ value,
|
|
|
+ token_id,
|
|
|
+ signature_secret: self.signature_secret,
|
|
|
+ }],
|
|
|
+ inputs: vec![],
|
|
|
+ outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
|
|
|
+ value,
|
|
|
+ token_id,
|
|
|
+ public: recipient,
|
|
|
+ serial: pallas::Base::random(&mut OsRng),
|
|
|
+ coin_blind: pallas::Base::random(&mut OsRng),
|
|
|
+ spend_hook,
|
|
|
+ user_data,
|
|
|
+ }],
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let func_call = builder.build(zk_bins)?;
|
|
|
+ let func_calls = vec![func_call];
|
|
|
+
|
|
|
+ let signatures = sign(vec![self.signature_secret], &func_calls);
|
|
|
+ Ok(Transaction { func_calls, signatures })
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-async fn start() -> Result<()> {
|
|
|
- // daod
|
|
|
- //
|
|
|
- let rpc_addr = Url::parse("tcp://127.0.0.1:7777")?;
|
|
|
- let mut demo = DaoDemo::new();
|
|
|
- /////////////////////////////////////////////////
|
|
|
- //// init()
|
|
|
- /////////////////////////////////////////////////
|
|
|
- demo.init()?;
|
|
|
- let client = JsonRpcInterface::new(demo);
|
|
|
+ fn airdrop(
|
|
|
+ &mut self,
|
|
|
+ value: u64,
|
|
|
+ token_id: pallas::Base,
|
|
|
+ recipient: PublicKey,
|
|
|
+ zk_bins: &ZkContractTable,
|
|
|
+ ) -> Result<Transaction> {
|
|
|
+ // Spend hook and user data disabled
|
|
|
+ let spend_hook = DrkSpendHook::from(0);
|
|
|
+ let user_data = DrkUserData::from(0);
|
|
|
|
|
|
- let rpc_interface = Arc::new(client);
|
|
|
+ let tx = self.transfer_tx(value, token_id, spend_hook, user_data, recipient, zk_bins)?;
|
|
|
|
|
|
- listen_and_serve(rpc_addr, rpc_interface).await?;
|
|
|
- Ok(())
|
|
|
+ Ok(tx)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[async_std::main]
|
|
|
@@ -869,7 +831,10 @@ async fn main() -> Result<()> {
|
|
|
ColorChoice::Auto,
|
|
|
)?;
|
|
|
|
|
|
- start().await?;
|
|
|
- // demo().await?;
|
|
|
+ let mut demo = Demo::new();
|
|
|
+ demo.init();
|
|
|
+
|
|
|
+ start_rpc(demo).await?;
|
|
|
+
|
|
|
Ok(())
|
|
|
}
|