| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328 |
- use std::{
- any::{Any, TypeId},
- time::Instant,
- };
- use incrementalmerkletree::Tree;
- use log::debug;
- use pasta_curves::{
- arithmetic::CurveAffine,
- group::{ff::Field, Curve, Group},
- pallas,
- };
- use rand::rngs::OsRng;
- use darkfi::{
- crypto::{
- keypair::{Keypair, PublicKey, SecretKey},
- proof::{ProvingKey, VerifyingKey},
- types::{DrkSpendHook, DrkUserData, DrkValue},
- util::{pedersen_commitment_u64, poseidon_hash},
- },
- zk::circuit::{BurnContract, MintContract},
- zkas::decoder::ZkBinary,
- };
- mod contract;
- mod note;
- mod util;
- use crate::{
- contract::{dao_contract, example_contract, money_contract},
- util::{sign, StateRegistry, Transaction, ZkContractTable},
- };
- // TODO: Anonymity leaks in this proof of concept:
- //
- // * Vote updates are linked to the proposal_bulla
- // * Nullifier of vote will link vote with the coin when it's spent
- // TODO: strategize and cleanup Result/Error usage
- // TODO: fix up code doc
- type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
- // #[derive(Eq, PartialEq)]
- // pub struct HashableBase(pub pallas::Base);
- // impl std::hash::Hash for HashableBase {
- // fn hash<H: Hasher>(&self, state: &mut H) {
- // let bytes = self.0.to_repr();
- // bytes.hash(state);
- // }
- // }
- // pub struct ZkBinaryContractInfo {
- // pub k_param: u32,
- // pub bincode: ZkBinary,
- // pub proving_key: ProvingKey,
- // pub verifying_key: VerifyingKey,
- // }
- // pub struct ZkNativeContractInfo {
- // pub proving_key: ProvingKey,
- // pub verifying_key: VerifyingKey,
- // }
- // pub enum ZkContractInfo {
- // Binary(ZkBinaryContractInfo),
- // Native(ZkNativeContractInfo),
- // }
- // pub struct ZkContractTable {
- // // Key will be a hash of zk binary contract on chain
- // table: HashMap<String, ZkContractInfo>,
- // }
- // impl ZkContractTable {
- // fn new() -> Self {
- // Self { table: HashMap::new() }
- // }
- // fn add_contract(&mut self, key: String, bincode: ZkBinary, k_param: u32) {
- // let witnesses = empty_witnesses(&bincode);
- // let circuit = ZkCircuit::new(witnesses, bincode.clone());
- // let proving_key = ProvingKey::build(k_param, &circuit);
- // let verifying_key = VerifyingKey::build(k_param, &circuit);
- // let info = ZkContractInfo::Binary(ZkBinaryContractInfo {
- // k_param,
- // bincode,
- // proving_key,
- // verifying_key,
- // });
- // self.table.insert(key, info);
- // }
- // fn add_native(&mut self, key: String, proving_key: ProvingKey, verifying_key: VerifyingKey) {
- // self.table.insert(
- // key,
- // ZkContractInfo::Native(ZkNativeContractInfo { proving_key, verifying_key }),
- // );
- // }
- // pub fn lookup(&self, key: &String) -> Option<&ZkContractInfo> {
- // self.table.get(key)
- // }
- // }
- // pub struct Transaction {
- // pub func_calls: Vec<FuncCall>,
- // pub signatures: Vec<Signature>,
- // }
- // impl Transaction {
- // /// Verify ZK contracts for the entire tx
- // /// In real code, we could parallelize this for loop
- // /// TODO: fix use of unwrap with Result type stuff
- // fn zk_verify(&self, zk_bins: &ZkContractTable) {
- // for func_call in &self.func_calls {
- // let proofs_public_vals = &func_call.call_data.zk_public_values();
- // assert_eq!(
- // proofs_public_vals.len(),
- // func_call.proofs.len(),
- // "proof_public_vals.len()={} and func_call.proofs.len()={} do not match",
- // proofs_public_vals.len(),
- // func_call.proofs.len()
- // );
- // for (i, (proof, (key, public_vals))) in
- // func_call.proofs.iter().zip(proofs_public_vals.iter()).enumerate()
- // {
- // match zk_bins.lookup(key).unwrap() {
- // ZkContractInfo::Binary(info) => {
- // let verifying_key = &info.verifying_key;
- // let verify_result = proof.verify(&verifying_key, public_vals);
- // assert!(verify_result.is_ok(), "verify proof[{}]='{}' failed", i, key);
- // }
- // ZkContractInfo::Native(info) => {
- // let verifying_key = &info.verifying_key;
- // let verify_result = proof.verify(&verifying_key, public_vals);
- // assert!(verify_result.is_ok(), "verify proof[{}]='{}' failed", i, key);
- // }
- // };
- // debug!(target: "demo", "zk_verify({}) passed [i={}]", key, i);
- // }
- // }
- // }
- // fn verify_sigs(&self) {
- // let mut unsigned_tx_data = vec![];
- // for (i, (func_call, signature)) in
- // self.func_calls.iter().zip(self.signatures.clone()).enumerate()
- // {
- // func_call.encode(&mut unsigned_tx_data).expect("failed to encode data");
- // let signature_pub_keys = func_call.call_data.signature_public_keys();
- // for signature_pub_key in signature_pub_keys {
- // let verify_result = signature_pub_key.verify(&unsigned_tx_data[..], &signature);
- // assert!(verify_result, "verify sigs[{}] failed", i);
- // }
- // debug!(target: "demo", "verify_sigs({}) passed", i);
- // }
- // }
- // }
- // fn sign(signature_secrets: Vec<SecretKey>, func_calls: &Vec<FuncCall>) -> Vec<Signature> {
- // let mut signatures = vec![];
- // let mut unsigned_tx_data = vec![];
- // for (_i, (signature_secret, func_call)) in
- // signature_secrets.iter().zip(func_calls.iter()).enumerate()
- // {
- // func_call.encode(&mut unsigned_tx_data).expect("failed to encode data");
- // let signature = signature_secret.sign(&unsigned_tx_data[..]);
- // signatures.push(signature);
- // }
- // signatures
- // }
- // type ContractId = pallas::Base;
- // type FuncId = pallas::Base;
- // pub struct FuncCall {
- // pub contract_id: ContractId,
- // pub func_id: FuncId,
- // pub call_data: Box<dyn CallDataBase>,
- // pub proofs: Vec<Proof>,
- // }
- // impl Encodable for FuncCall {
- // fn encode<W: std::io::Write>(&self, mut w: W) -> std::result::Result<usize, darkfi::Error> {
- // let mut len = 0;
- // len += self.contract_id.encode(&mut w)?;
- // len += self.func_id.encode(&mut w)?;
- // len += self.proofs.encode(&mut w)?;
- // len += self.call_data.encode_bytes(&mut w)?;
- // Ok(len)
- // }
- // }
- // pub trait CallDataBase {
- // // Public values for verifying the proofs
- // // Needed so we can convert internal types so they can be used in Proof::verify()
- // fn zk_public_values(&self) -> Vec<(String, Vec<DrkCircuitField>)>;
- // // For upcasting to CallData itself so it can be read in state_transition()
- // fn as_any(&self) -> &dyn Any;
- // // Public keys we will use to verify transaction signatures.
- // fn signature_public_keys(&self) -> Vec<PublicKey>;
- // fn encode_bytes(
- // &self,
- // writer: &mut dyn std::io::Write,
- // ) -> std::result::Result<usize, darkfi::Error>;
- // }
- // type GenericContractState = Box<dyn Any>;
- // pub struct StateRegistry {
- // pub states: HashMap<HashableBase, GenericContractState>,
- // }
- // impl StateRegistry {
- // fn new() -> Self {
- // Self { states: HashMap::new() }
- // }
- // fn register(&mut self, contract_id: ContractId, state: GenericContractState) {
- // debug!(target: "StateRegistry::register()", "contract_id: {:?}", contract_id);
- // self.states.insert(HashableBase(contract_id), state);
- // }
- // pub fn lookup_mut<'a, S: 'static>(&'a mut self, contract_id: ContractId) -> Option<&'a mut S> {
- // self.states.get_mut(&HashableBase(contract_id)).and_then(|state| state.downcast_mut())
- // }
- // pub fn lookup<'a, S: 'static>(&'a self, contract_id: ContractId) -> Option<&'a S> {
- // self.states.get(&HashableBase(contract_id)).and_then(|state| state.downcast_ref())
- // }
- // }
- // pub trait UpdateBase {
- // fn apply(self: Box<Self>, states: &mut StateRegistry);
- // }
- ///////////////////////////////////////////////////
- ///// Example contract
- ///////////////////////////////////////////////////
- pub async fn example() -> Result<()> {
- debug!(target: "demo", "Stage 0. Example contract");
- // Lookup table for smart contract states
- let mut states = StateRegistry::new();
- // Initialize ZK binary table
- let mut zk_bins = ZkContractTable::new();
- let zk_example_foo_bincode = include_bytes!("proof/foo.zk.bin");
- let zk_example_foo_bin = ZkBinary::decode(zk_example_foo_bincode)?;
- zk_bins.add_contract("example-foo".to_string(), zk_example_foo_bin, 13);
- let example_state = example_contract::state::State::new();
- states.register(*example_contract::CONTRACT_ID, example_state);
- //// Wallet
- let foo = example_contract::foo::wallet::Foo { a: 5, b: 10 };
- let signature_secret = SecretKey::random(&mut OsRng);
- let builder = example_contract::foo::wallet::Builder { foo, signature_secret };
- let func_call = builder.build(&zk_bins);
- let func_calls = vec![func_call];
- let signatures = sign(vec![signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // Validate all function calls in the tx
- for (idx, func_call) in tx.func_calls.iter().enumerate() {
- if func_call.func_id == *example_contract::foo::FUNC_ID {
- debug!("example_contract::foo::state_transition()");
- let update = example_contract::foo::validate::state_transition(&states, idx, &tx)
- .expect("example_contract::foo::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- Ok(())
- }
- #[async_std::main]
- async fn main() -> Result<()> {
- // Example smart contract
- //// TODO: this will be moved to a different file
- example().await?;
- // Money parameters
- let xdrk_supply = 1_000_000;
- let xdrk_token_id = pallas::Base::random(&mut OsRng);
- // Governance token parameters
- let gdrk_supply = 1_000_000;
- let gdrk_token_id = pallas::Base::random(&mut OsRng);
- // DAO parameters
- let dao_proposer_limit = 110;
- let dao_quorum = 110;
- let dao_approval_ratio_quot = 1;
- let dao_approval_ratio_base = 2;
- // Lookup table for smart contract states
- let mut states = StateRegistry::new();
- // Initialize ZK binary table
- let mut zk_bins = ZkContractTable::new();
- 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)?;
- 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());
- zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
- 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)?;
- 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)?;
- 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)?;
- 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)?;
- 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)?;
- zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
- // State for money contracts
- 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);
- ///////////////////////////////////////////////////
- let money_state =
- money_contract::state::State::new(cashier_signature_public, faucet_signature_public);
- states.register(*money_contract::CONTRACT_ID, money_state);
- /////////////////////////////////////////////////////
- let dao_state = dao_contract::State::new();
- states.register(*dao_contract::CONTRACT_ID, dao_state);
- /////////////////////////////////////////////////////
- ////// Create the DAO bulla
- /////////////////////////////////////////////////////
- debug!(target: "demo", "Stage 1. Creating DAO bulla");
- //// Wallet
- //// Setup the DAO
- let dao_keypair = Keypair::random(&mut OsRng);
- let dao_bulla_blind = pallas::Base::random(&mut OsRng);
- let signature_secret = SecretKey::random(&mut OsRng);
- // Create DAO mint tx
- let builder = dao_contract::mint::wallet::Builder {
- dao_proposer_limit,
- dao_quorum,
- dao_approval_ratio_quot,
- dao_approval_ratio_base,
- gov_token_id: gdrk_token_id,
- dao_pubkey: dao_keypair.public,
- dao_bulla_blind,
- _signature_secret: signature_secret,
- };
- let func_call = builder.build(&zk_bins);
- let func_calls = vec![func_call];
- let signatures = sign(vec![signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // 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
- // functions based off the func_id
- if func_call.func_id == *dao_contract::mint::FUNC_ID {
- debug!("dao_contract::mint::state_transition()");
- let update = dao_contract::mint::validate::state_transition(&states, idx, &tx)
- .expect("dao_contract::mint::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- // Wallet stuff
- // In your wallet, wait until you see the tx confirmed before doing anything below
- // So for example keep track of tx hash
- //assert_eq!(tx.hash(), tx_hash);
- // We need to witness() the value in our local merkle tree
- // Must be called as soon as this DAO bulla is added to the state
- let dao_leaf_position = {
- let state = states.lookup_mut::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
- state.dao_tree.witness().unwrap()
- };
- // It might just be easier to hash it ourselves from keypair and blind...
- 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()
- };
- debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
- ///////////////////////////////////////////////////
- //// Mint the initial supply of treasury token
- //// and send it all to the DAO directly
- ///////////////////////////////////////////////////
- debug!(target: "demo", "Stage 2. Minting treasury token");
- let state = states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
- state.wallet_cache.track(dao_keypair.secret);
- //// Wallet
- // Address of deployed contract in our example is dao_contract::exec::FUNC_ID
- // This field is public, you can see it's being sent to a DAO
- // but nothing else is visible.
- //
- // In the python code we wrote:
- //
- // spend_hook = b"0xdao_ruleset"
- //
- let spend_hook = *dao_contract::exec::FUNC_ID;
- // The user_data can be a simple hash of the items passed into the ZK proof
- // up to corresponding linked ZK proof to interpret however they need.
- // In out case, it's the bulla for the DAO
- let user_data = dao_bulla.0;
- let builder = money_contract::transfer::wallet::Builder {
- clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
- value: xdrk_supply,
- token_id: xdrk_token_id,
- signature_secret: cashier_signature_secret,
- }],
- inputs: vec![],
- outputs: vec![money_contract::transfer::wallet::BuilderOutputInfo {
- value: xdrk_supply,
- token_id: xdrk_token_id,
- public: dao_keypair.public,
- 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![cashier_signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // 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
- // functions based off the func_id
- if func_call.func_id == *money_contract::transfer::FUNC_ID {
- debug!("money_contract::transfer::state_transition()");
- let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
- .expect("money_contract::transfer::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- // DAO reads the money received from the encrypted note
- let state = states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
- let mut recv_coins = state.wallet_cache.get_received(&dao_keypair.secret);
- assert_eq!(recv_coins.len(), 1);
- let dao_recv_coin = recv_coins.pop().unwrap();
- let treasury_note = dao_recv_coin.note;
- // Check the actual coin received is valid before accepting it
- let coords = dao_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,
- ]);
- assert_eq!(coin, dao_recv_coin.coin.0);
- assert_eq!(treasury_note.spend_hook, *dao_contract::exec::FUNC_ID);
- assert_eq!(treasury_note.user_data, dao_bulla.0);
- debug!("DAO received a coin worth {} xDRK", treasury_note.value);
- ///////////////////////////////////////////////////
- //// Mint the governance token
- //// Send it to three hodlers
- ///////////////////////////////////////////////////
- debug!(target: "demo", "Stage 3. Minting governance token");
- //// Wallet
- // Hodler 1
- let gov_keypair_1 = Keypair::random(&mut OsRng);
- // Hodler 2
- let gov_keypair_2 = Keypair::random(&mut OsRng);
- // Hodler 3: the tiebreaker
- let gov_keypair_3 = Keypair::random(&mut OsRng);
- let state = states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
- state.wallet_cache.track(gov_keypair_1.secret);
- state.wallet_cache.track(gov_keypair_2.secret);
- state.wallet_cache.track(gov_keypair_3.secret);
- let gov_keypairs = vec![gov_keypair_1, gov_keypair_2, gov_keypair_3];
- // Spend hook and user data disabled
- let spend_hook = DrkSpendHook::from(0);
- let user_data = DrkUserData::from(0);
- let output1 = money_contract::transfer::wallet::BuilderOutputInfo {
- value: 400000,
- token_id: gdrk_token_id,
- public: gov_keypair_1.public,
- serial: pallas::Base::random(&mut OsRng),
- coin_blind: pallas::Base::random(&mut OsRng),
- spend_hook,
- user_data,
- };
- let output2 = money_contract::transfer::wallet::BuilderOutputInfo {
- value: 400000,
- token_id: gdrk_token_id,
- public: gov_keypair_2.public,
- serial: pallas::Base::random(&mut OsRng),
- coin_blind: pallas::Base::random(&mut OsRng),
- spend_hook,
- user_data,
- };
- let output3 = money_contract::transfer::wallet::BuilderOutputInfo {
- value: 200000,
- token_id: gdrk_token_id,
- public: gov_keypair_3.public,
- serial: pallas::Base::random(&mut OsRng),
- coin_blind: pallas::Base::random(&mut OsRng),
- spend_hook,
- user_data,
- };
- assert!(2 * 400000 + 200000 == gdrk_supply);
- let builder = money_contract::transfer::wallet::Builder {
- clear_inputs: vec![money_contract::transfer::wallet::BuilderClearInputInfo {
- value: gdrk_supply,
- token_id: gdrk_token_id,
- signature_secret: cashier_signature_secret,
- }],
- inputs: vec![],
- outputs: vec![output1, output2, output3],
- };
- let func_call = builder.build(&zk_bins)?;
- let func_calls = vec![func_call];
- let signatures = sign(vec![cashier_signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // 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
- // functions based off the func_id
- if func_call.func_id == *money_contract::transfer::FUNC_ID {
- debug!("money_contract::transfer::state_transition()");
- let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
- .expect("money_contract::transfer::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- let mut gov_recv = vec![None, None, None];
- // Check that each person received one coin
- for (i, key) in gov_keypairs.iter().enumerate() {
- let gov_recv_coin = {
- let state =
- states.lookup_mut::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
- let mut recv_coins = state.wallet_cache.get_received(&key.secret);
- assert_eq!(recv_coins.len(), 1);
- let recv_coin = recv_coins.pop().unwrap();
- let note = &recv_coin.note;
- assert_eq!(note.token_id, gdrk_token_id);
- // Normal payment
- assert_eq!(note.spend_hook, pallas::Base::from(0));
- assert_eq!(note.user_data, pallas::Base::from(0));
- let coords = key.public.0.to_affine().coordinates().unwrap();
- let coin = poseidon_hash::<8>([
- *coords.x(),
- *coords.y(),
- DrkValue::from(note.value),
- note.token_id,
- note.serial,
- note.spend_hook,
- note.user_data,
- note.coin_blind,
- ]);
- assert_eq!(coin, recv_coin.coin.0);
- debug!("Holder{} received a coin worth {} gDRK", i, note.value);
- recv_coin
- };
- gov_recv[i] = Some(gov_recv_coin);
- }
- // unwrap them for this demo
- let gov_recv: Vec<_> = gov_recv.into_iter().map(|r| r.unwrap()).collect();
- ///////////////////////////////////////////////////
- // DAO rules:
- // 1. gov token IDs must match on all inputs
- // 2. proposals must be submitted by minimum amount
- // 3. all votes >= quorum
- // 4. outcome > approval_ratio
- // 5. structure of outputs
- // output 0: value and address
- // output 1: change address
- ///////////////////////////////////////////////////
- ///////////////////////////////////////////////////
- // Propose the vote
- // In order to make a valid vote, first the proposer must
- // meet a criteria for a minimum number of gov tokens
- ///////////////////////////////////////////////////
- debug!(target: "demo", "Stage 4. Propose the vote");
- //// Wallet
- // TODO: look into proposal expiry once time for voting has finished
- let user_keypair = Keypair::random(&mut OsRng);
- 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 = gov_recv[0].leaf_position.clone();
- let root = tree.root(0).unwrap();
- let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
- (leaf_position, merkle_path)
- };
- // TODO: is it possible for an invalid transfer() to be constructed on exec()?
- // need to look into this
- let signature_secret = SecretKey::random(&mut OsRng);
- let input = dao_contract::propose::wallet::BuilderInput {
- secret: gov_keypair_1.secret,
- note: gov_recv[0].note.clone(),
- leaf_position: money_leaf_position,
- merkle_path: money_merkle_path,
- signature_secret,
- };
- let (dao_merkle_path, dao_merkle_root) = {
- let state = states.lookup::<dao_contract::State>(*dao_contract::CONTRACT_ID).unwrap();
- let tree = &state.dao_tree;
- let root = tree.root(0).unwrap();
- let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
- (merkle_path, root)
- };
- let dao_params = dao_contract::mint::wallet::DaoParams {
- proposer_limit: dao_proposer_limit,
- quorum: dao_quorum,
- approval_ratio_base: dao_approval_ratio_base,
- approval_ratio_quot: dao_approval_ratio_quot,
- gov_token_id: gdrk_token_id,
- public_key: dao_keypair.public,
- bulla_blind: dao_bulla_blind,
- };
- let proposal = dao_contract::propose::wallet::Proposal {
- dest: user_keypair.public,
- amount: 1000,
- serial: pallas::Base::random(&mut OsRng),
- token_id: xdrk_token_id,
- blind: pallas::Base::random(&mut OsRng),
- };
- let builder = dao_contract::propose::wallet::Builder {
- inputs: vec![input],
- proposal: proposal.clone(),
- dao: dao_params.clone(),
- dao_leaf_position,
- dao_merkle_path,
- dao_merkle_root,
- };
- let func_call = builder.build(&zk_bins);
- let func_calls = vec![func_call];
- let signatures = sign(vec![signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // Validate all function calls in the tx
- for (idx, func_call) in tx.func_calls.iter().enumerate() {
- if func_call.func_id == *dao_contract::propose::FUNC_ID {
- debug!(target: "demo", "dao_contract::propose::state_transition()");
- let update = dao_contract::propose::validate::state_transition(&states, idx, &tx)
- .expect("dao_contract::propose::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- // Read received proposal
- let (proposal, proposal_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::propose::validate::CallData>()
- );
- let call_data =
- call_data.downcast_ref::<dao_contract::propose::validate::CallData>().unwrap();
- let header = &call_data.header;
- let note: dao_contract::propose::wallet::Note =
- header.enc_note.decrypt(&dao_keypair.secret).unwrap();
- // TODO: check it belongs to DAO bulla
- // Return the proposal info
- (note.proposal, call_data.header.proposal_bulla)
- };
- debug!(target: "demo", "Proposal now active!");
- debug!(target: "demo", " destination: {:?}", proposal.dest);
- debug!(target: "demo", " amount: {}", proposal.amount);
- debug!(target: "demo", " token_id: {:?}", proposal.token_id);
- debug!(target: "demo", " dao_bulla: {:?}", dao_bulla.0);
- debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
- ///////////////////////////////////////////////////
- // Proposal is accepted!
- // Start the voting
- ///////////////////////////////////////////////////
- // Copying these schizo comments from python code:
- // Lets the voting begin
- // Voters have access to the proposal and dao data
- // vote_state = VoteState()
- // We don't need to copy nullifier set because it is checked from gov_state
- // in vote_state_transition() anyway
- //
- // TODO: what happens if voters don't unblind their vote
- // Answer:
- // 1. there is a time limit
- // 2. both the MPC or users can unblind
- //
- // TODO: bug if I vote then send money, then we can double vote
- // TODO: all timestamps missing
- // - timelock (future voting starts in 2 days)
- // Fix: use nullifiers from money gov state only from
- // beginning of gov period
- // Cannot use nullifiers from before voting period
- debug!(target: "demo", "Stage 5. Start voting");
- // We were previously saving updates here for testing
- // let mut updates = vec![];
- // User 1: YES
- 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 = gov_recv[0].leaf_position.clone();
- let root = tree.root(0).unwrap();
- let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
- (leaf_position, merkle_path)
- };
- let signature_secret = SecretKey::random(&mut OsRng);
- let input = dao_contract::vote::wallet::BuilderInput {
- secret: gov_keypair_1.secret,
- note: gov_recv[0].note.clone(),
- leaf_position: money_leaf_position,
- merkle_path: money_merkle_path,
- signature_secret,
- };
- let vote_option: bool = true;
- assert!(vote_option == true || vote_option == false);
- // We create a new keypair to encrypt the vote.
- // For the demo MVP, you can just use the dao_keypair secret
- let vote_keypair_1 = Keypair::random(&mut OsRng);
- let builder = dao_contract::vote::wallet::Builder {
- inputs: vec![input],
- vote: dao_contract::vote::wallet::Vote {
- vote_option,
- vote_option_blind: pallas::Scalar::random(&mut OsRng),
- },
- vote_keypair: vote_keypair_1,
- proposal: proposal.clone(),
- dao: dao_params.clone(),
- };
- debug!(target: "demo", "build()...");
- let func_call = builder.build(&zk_bins);
- let func_calls = vec![func_call];
- let signatures = sign(vec![signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // Validate all function calls in the tx
- for (idx, func_call) in tx.func_calls.iter().enumerate() {
- if func_call.func_id == *dao_contract::vote::FUNC_ID {
- debug!(target: "demo", "dao_contract::vote::state_transition()");
- let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
- .expect("dao_contract::vote::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- // Secret vote info. Needs to be revealed at some point.
- // TODO: look into verifiable encryption for notes
- // TODO: look into timelock puzzle as a possibility
- let vote_note_1 = {
- 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::vote::validate::CallData>());
- let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
- let header = &call_data.header;
- let note: dao_contract::vote::wallet::Note =
- header.enc_note.decrypt(&vote_keypair_1.secret).unwrap();
- note
- };
- debug!(target: "demo", "User 1 voted!");
- debug!(target: "demo", " vote_option: {}", vote_note_1.vote.vote_option);
- debug!(target: "demo", " value: {}", vote_note_1.vote_value);
- // User 2: NO
- 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 = gov_recv[1].leaf_position.clone();
- let root = tree.root(0).unwrap();
- let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
- (leaf_position, merkle_path)
- };
- let signature_secret = SecretKey::random(&mut OsRng);
- let input = dao_contract::vote::wallet::BuilderInput {
- secret: gov_keypair_2.secret,
- note: gov_recv[1].note.clone(),
- leaf_position: money_leaf_position,
- merkle_path: money_merkle_path,
- signature_secret,
- };
- let vote_option: bool = false;
- assert!(vote_option == true || vote_option == false);
- // We create a new keypair to encrypt the vote.
- let vote_keypair_2 = Keypair::random(&mut OsRng);
- let builder = dao_contract::vote::wallet::Builder {
- inputs: vec![input],
- vote: dao_contract::vote::wallet::Vote {
- vote_option,
- vote_option_blind: pallas::Scalar::random(&mut OsRng),
- },
- vote_keypair: vote_keypair_2,
- proposal: proposal.clone(),
- dao: dao_params.clone(),
- };
- debug!(target: "demo", "build()...");
- let func_call = builder.build(&zk_bins);
- let func_calls = vec![func_call];
- let signatures = sign(vec![signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // Validate all function calls in the tx
- for (idx, func_call) in tx.func_calls.iter().enumerate() {
- if func_call.func_id == *dao_contract::vote::FUNC_ID {
- debug!(target: "demo", "dao_contract::vote::state_transition()");
- let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
- .expect("dao_contract::vote::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- // Secret vote info. Needs to be revealed at some point.
- // TODO: look into verifiable encryption for notes
- // TODO: look into timelock puzzle as a possibility
- let vote_note_2 = {
- 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::vote::validate::CallData>());
- let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
- let header = &call_data.header;
- let note: dao_contract::vote::wallet::Note =
- header.enc_note.decrypt(&vote_keypair_2.secret).unwrap();
- note
- };
- debug!(target: "demo", "User 2 voted!");
- debug!(target: "demo", " vote_option: {}", vote_note_2.vote.vote_option);
- debug!(target: "demo", " value: {}", vote_note_2.vote_value);
- // User 3: YES
- 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 = gov_recv[2].leaf_position.clone();
- let root = tree.root(0).unwrap();
- let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
- (leaf_position, merkle_path)
- };
- let signature_secret = SecretKey::random(&mut OsRng);
- let input = dao_contract::vote::wallet::BuilderInput {
- secret: gov_keypair_3.secret,
- note: gov_recv[2].note.clone(),
- leaf_position: money_leaf_position,
- merkle_path: money_merkle_path,
- signature_secret,
- };
- let vote_option: bool = true;
- assert!(vote_option == true || vote_option == false);
- // We create a new keypair to encrypt the vote.
- let vote_keypair_3 = Keypair::random(&mut OsRng);
- let builder = dao_contract::vote::wallet::Builder {
- inputs: vec![input],
- vote: dao_contract::vote::wallet::Vote {
- vote_option,
- vote_option_blind: pallas::Scalar::random(&mut OsRng),
- },
- vote_keypair: vote_keypair_3,
- proposal: proposal.clone(),
- dao: dao_params.clone(),
- };
- debug!(target: "demo", "build()...");
- let func_call = builder.build(&zk_bins);
- let func_calls = vec![func_call];
- let signatures = sign(vec![signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- //// Validator
- let mut updates = vec![];
- // Validate all function calls in the tx
- for (idx, func_call) in tx.func_calls.iter().enumerate() {
- if func_call.func_id == *dao_contract::vote::FUNC_ID {
- debug!(target: "demo", "dao_contract::vote::state_transition()");
- let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
- .expect("dao_contract::vote::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- // Secret vote info. Needs to be revealed at some point.
- // TODO: look into verifiable encryption for notes
- // TODO: look into timelock puzzle as a possibility
- let vote_note_3 = {
- 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::vote::validate::CallData>());
- let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
- let header = &call_data.header;
- let note: dao_contract::vote::wallet::Note =
- header.enc_note.decrypt(&vote_keypair_3.secret).unwrap();
- note
- };
- debug!(target: "demo", "User 3 voted!");
- debug!(target: "demo", " vote_option: {}", vote_note_3.vote.vote_option);
- debug!(target: "demo", " value: {}", vote_note_3.vote_value);
- // Every votes produces a semi-homomorphic encryption of their vote.
- // Which is either yes or no
- // We copy the state tree for the governance token so coins can be used
- // to vote on other proposals at the same time.
- // With their vote, they produce a ZK proof + nullifier
- // The votes are unblinded by MPC to a selected party at the end of the
- // voting period.
- // (that's if we want votes to be hidden during voting)
- let mut yes_votes_value = 0;
- let mut yes_votes_blind = pallas::Scalar::from(0);
- let mut yes_votes_commit = pallas::Point::identity();
- let mut all_votes_value = 0;
- let mut all_votes_blind = pallas::Scalar::from(0);
- let mut all_votes_commit = pallas::Point::identity();
- // We were previously saving votes to a Vec<Update> for testing.
- // However since Update is now UpdateBase it gets moved into update.apply().
- // So we need to think of another way to run these tests.
- //assert!(updates.len() == 3);
- for (i, note /* update*/) in [vote_note_1, vote_note_2, vote_note_3]
- .iter() /*.zip(updates)*/
- .enumerate()
- {
- let vote_commit = pedersen_commitment_u64(note.vote_value, note.vote_value_blind);
- //assert!(update.value_commit == all_vote_value_commit);
- all_votes_commit += vote_commit;
- all_votes_blind += note.vote_value_blind;
- let yes_vote_commit = pedersen_commitment_u64(
- note.vote.vote_option as u64 * note.vote_value,
- note.vote.vote_option_blind,
- );
- //assert!(update.yes_vote_commit == yes_vote_commit);
- yes_votes_commit += yes_vote_commit;
- yes_votes_blind += note.vote.vote_option_blind;
- let vote_option = note.vote.vote_option;
- if vote_option {
- yes_votes_value += note.vote_value;
- }
- all_votes_value += note.vote_value;
- let vote_result: String = if vote_option { "yes".to_string() } else { "no".to_string() };
- debug!("Voter {} voted {}", i, vote_result);
- }
- debug!("Outcome = {} / {}", yes_votes_value, all_votes_value);
- assert!(all_votes_commit == pedersen_commitment_u64(all_votes_value, all_votes_blind));
- assert!(yes_votes_commit == pedersen_commitment_u64(yes_votes_value, yes_votes_blind));
- ///////////////////////////////////////////////////
- // Execute the vote
- ///////////////////////////////////////////////////
- //// Wallet
- // Used to export user_data from this coin so it can be accessed by DAO::exec()
- let user_data_blind = pallas::Base::random(&mut OsRng);
- let user_serial = pallas::Base::random(&mut OsRng);
- let user_coin_blind = pallas::Base::random(&mut OsRng);
- let dao_serial = pallas::Base::random(&mut OsRng);
- let dao_coin_blind = pallas::Base::random(&mut OsRng);
- let input_value = treasury_note.value;
- let input_value_blind = pallas::Scalar::random(&mut OsRng);
- let tx_signature_secret = SecretKey::random(&mut OsRng);
- let exec_signature_secret = SecretKey::random(&mut OsRng);
- let (treasury_leaf_position, treasury_merkle_path) = {
- let state = states.lookup::<money_contract::State>(*money_contract::CONTRACT_ID).unwrap();
- let tree = &state.tree;
- let leaf_position = dao_recv_coin.leaf_position.clone();
- let root = tree.root(0).unwrap();
- let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
- (leaf_position, merkle_path)
- };
- let input = money_contract::transfer::wallet::BuilderInputInfo {
- leaf_position: treasury_leaf_position,
- merkle_path: treasury_merkle_path,
- secret: dao_keypair.secret,
- note: treasury_note,
- user_data_blind,
- value_blind: input_value_blind,
- signature_secret: tx_signature_secret,
- };
- let builder = money_contract::transfer::wallet::Builder {
- clear_inputs: vec![],
- inputs: vec![input],
- outputs: vec![
- // Sending money
- money_contract::transfer::wallet::BuilderOutputInfo {
- value: 1000,
- token_id: xdrk_token_id,
- public: user_keypair.public,
- serial: proposal.serial,
- coin_blind: proposal.blind,
- spend_hook: pallas::Base::from(0),
- user_data: pallas::Base::from(0),
- },
- // Change back to DAO
- money_contract::transfer::wallet::BuilderOutputInfo {
- value: xdrk_supply - 1000,
- token_id: xdrk_token_id,
- public: dao_keypair.public,
- serial: dao_serial,
- coin_blind: dao_coin_blind,
- spend_hook: *dao_contract::exec::FUNC_ID,
- user_data: proposal_bulla,
- },
- ],
- };
- let transfer_func_call = builder.build(&zk_bins)?;
- let builder = dao_contract::exec::wallet::Builder {
- proposal,
- dao: dao_params,
- yes_votes_value,
- all_votes_value,
- yes_votes_blind,
- all_votes_blind,
- user_serial,
- user_coin_blind,
- dao_serial,
- dao_coin_blind,
- input_value,
- input_value_blind,
- hook_dao_exec: *dao_contract::exec::FUNC_ID,
- signature_secret: exec_signature_secret,
- };
- let exec_func_call = builder.build(&zk_bins);
- let func_calls = vec![transfer_func_call, exec_func_call];
- let signatures = sign(vec![tx_signature_secret, exec_signature_secret], &func_calls);
- let tx = Transaction { func_calls, signatures };
- {
- // Now the spend_hook field specifies the function DAO::exec()
- // so Money::transfer() must also be combined with DAO::exec()
- assert_eq!(tx.func_calls.len(), 2);
- let transfer_func_call = &tx.func_calls[0];
- let transfer_call_data = transfer_func_call.call_data.as_any();
- assert_eq!(
- (&*transfer_call_data).type_id(),
- TypeId::of::<money_contract::transfer::validate::CallData>()
- );
- let transfer_call_data =
- transfer_call_data.downcast_ref::<money_contract::transfer::validate::CallData>();
- let transfer_call_data = transfer_call_data.unwrap();
- // At least one input has this field value which means DAO::exec() is invoked.
- assert_eq!(transfer_call_data.inputs.len(), 1);
- let input = &transfer_call_data.inputs[0];
- assert_eq!(input.revealed.spend_hook, *dao_contract::exec::FUNC_ID);
- let user_data_enc = poseidon_hash::<2>([dao_bulla.0, user_data_blind]);
- assert_eq!(input.revealed.user_data_enc, user_data_enc);
- }
- //// Validator
- let mut updates = vec![];
- // Validate all function calls in the tx
- for (idx, func_call) in tx.func_calls.iter().enumerate() {
- if func_call.func_id == *dao_contract::exec::FUNC_ID {
- debug!("dao_contract::exec::state_transition()");
- let update = dao_contract::exec::validate::state_transition(&states, idx, &tx)
- .expect("dao_contract::exec::validate::state_transition() failed!");
- updates.push(update);
- } else if func_call.func_id == *money_contract::transfer::FUNC_ID {
- debug!("money_contract::transfer::state_transition()");
- let update = money_contract::transfer::validate::state_transition(&states, idx, &tx)
- .expect("money_contract::transfer::validate::state_transition() failed!");
- updates.push(update);
- }
- }
- // Atomically apply all changes
- for update in updates {
- update.apply(&mut states);
- }
- // Other stuff
- tx.zk_verify(&zk_bins);
- tx.verify_sigs();
- //// Wallet
- Ok(())
- }
|