From e02daace777f92ea25250a59c1080faaf93d1499 Mon Sep 17 00:00:00 2001 From: x Date: Mon, 26 Dec 2022 11:09:59 +0100 Subject: [PATCH] dao-test: mint gov token --- src/contract/dao/tests/integration.rs | 164 +++++++++++++++++++++++++- src/contract/money/tests/harness.rs | 21 ++++ 2 files changed, 180 insertions(+), 5 deletions(-) diff --git a/src/contract/dao/tests/integration.rs b/src/contract/dao/tests/integration.rs index 89019717a..6290b9ed5 100644 --- a/src/contract/dao/tests/integration.rs +++ b/src/contract/dao/tests/integration.rs @@ -145,12 +145,19 @@ async fn integration_test() -> Result<()> { cache.track(dao_th.dao_kp.secret); - // We use this to receive coins - //let mut cache = WalletCache::new(); - - //let (params, proofs) = builder.build(&zk_bins)?; + // Address of deployed contract in our example is dao::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" + // // TODO: this should be the contract/func ID let spend_hook = pallas::Base::from(110); + // 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.inner(); let builder = money_client::Builder { @@ -234,7 +241,154 @@ async fn integration_test() -> Result<()> { debug!("DAO received a coin worth {} xDRK", treasury_note.value); - /////////////////////////////////////////////////// + // ======================================================= + // Money::Transfer + // + // Mint the governance token + // Send it to three hodlers + // ======================================================= + debug!(target: "demo", "Stage 3. Minting governance token"); + + cache.track(money_th.alice_kp.secret); + cache.track(money_th.bob_kp.secret); + cache.track(money_th.charlie_kp.secret); + + // Spend hook and user data disabled + let spend_hook = pallas::Base::from(0); + let user_data = pallas::Base::from(0); + + let output1 = money_client::BuilderOutputInfo { + value: 400000, + token_id: gdrk_token_id, + public: money_th.alice_kp.public, + serial: pallas::Base::random(&mut OsRng), + coin_blind: pallas::Base::random(&mut OsRng), + spend_hook, + user_data, + }; + + let output2 = money_client::BuilderOutputInfo { + value: 400000, + token_id: gdrk_token_id, + public: money_th.bob_kp.public, + serial: pallas::Base::random(&mut OsRng), + coin_blind: pallas::Base::random(&mut OsRng), + spend_hook, + user_data, + }; + + let output3 = money_client::BuilderOutputInfo { + value: 200000, + token_id: gdrk_token_id, + public: money_th.charlie_kp.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_client::Builder { + clear_inputs: vec![money_client::BuilderClearInputInfo { + value: gdrk_supply, + token_id: gdrk_token_id, + // This might be different for various tokens but lets reuse it here + signature_secret: money_th.faucet_kp.secret, + }], + inputs: vec![], + outputs: vec![output1, output2, output3], + }; + let (params, proofs) = builder.build( + &money_th.mint_zkbin, + &money_th.mint_pk, + &money_th.burn_zkbin, + &money_th.burn_pk, + )?; + + let contract_id = *MONEY_CONTRACT_ID; + + let mut data = vec![MoneyFunction::Transfer as u8]; + params.encode(&mut data)?; + let calls = vec![ContractCall { contract_id, data }]; + let proofs = vec![proofs]; + let mut tx = Transaction { calls, proofs, signatures: vec![] }; + let sigs = tx.create_sigs(&mut OsRng, &vec![money_th.faucet_kp.secret])?; + tx.signatures = vec![sigs]; + + money_th.faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?; + money_th.faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin)); + + // Wallet + { + assert_eq!(tx.calls.len(), 1); + let calldata = &tx.calls[0].data; + let params_data = &calldata[1..]; + let params: MoneyTransferParams = Decodable::decode(params_data)?; + + for output in params.outputs { + let coin = output.coin; + let enc_note = + EncryptedNote { ciphertext: output.ciphertext, ephem_public: output.ephem_public }; + let coin = Coin(coin); + cache.try_decrypt_note(coin, &enc_note); + } + } + + let gov_keypairs = vec![money_th.alice_kp, money_th.bob_kp, money_th.charlie_kp]; + 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 mut recv_coins = 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 (pub_x, pub_y) = key.public.xy(); + let coin = poseidon_hash::<8>([ + pub_x, + pub_y, + pallas::Base::from(note.value), + note.token_id.inner(), + 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::Propose + // + // Propose the vote + // In order to make a valid vote, first the proposer must + // meet a criteria for a minimum number of gov tokens + // + // 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 + // ======================================================= + debug!(target: "demo", "Stage 4. Propose the vote"); Ok(()) } diff --git a/src/contract/money/tests/harness.rs b/src/contract/money/tests/harness.rs index b89c67712..ac75008c0 100644 --- a/src/contract/money/tests/harness.rs +++ b/src/contract/money/tests/harness.rs @@ -70,10 +70,12 @@ pub struct MoneyTestHarness { pub faucet_kp: Keypair, pub alice_kp: Keypair, pub bob_kp: Keypair, + pub charlie_kp: Keypair, pub faucet_pubkeys: Vec, pub faucet_state: ValidatorStatePtr, pub alice_state: ValidatorStatePtr, pub bob_state: ValidatorStatePtr, + pub charlie_state: ValidatorStatePtr, pub money_contract_id: ContractId, pub proving_keys: HashMap<[u8; 32], Vec<(&'static str, ProvingKey)>>, pub mint_zkbin: ZkBinary, @@ -83,6 +85,7 @@ pub struct MoneyTestHarness { pub faucet_merkle_tree: BridgeTree, pub alice_merkle_tree: BridgeTree, pub bob_merkle_tree: BridgeTree, + pub charlie_merkle_tree: BridgeTree, } impl MoneyTestHarness { @@ -90,15 +93,18 @@ impl MoneyTestHarness { let faucet_kp = Keypair::random(&mut OsRng); let alice_kp = Keypair::random(&mut OsRng); let bob_kp = Keypair::random(&mut OsRng); + let charlie_kp = Keypair::random(&mut OsRng); let faucet_pubkeys = vec![faucet_kp.public]; let faucet_wallet = WalletDb::new("sqlite::memory:", "foo").await?; let alice_wallet = WalletDb::new("sqlite::memory:", "foo").await?; let bob_wallet = WalletDb::new("sqlite::memory:", "foo").await?; + let charlie_wallet = WalletDb::new("sqlite::memory:", "foo").await?; let faucet_sled_db = sled::Config::new().temporary(true).open()?; let alice_sled_db = sled::Config::new().temporary(true).open()?; let bob_sled_db = sled::Config::new().temporary(true).open()?; + let charlie_sled_db = sled::Config::new().temporary(true).open()?; let faucet_state = ValidatorState::new( &faucet_sled_db, @@ -133,6 +139,17 @@ impl MoneyTestHarness { ) .await?; + let charlie_state = ValidatorState::new( + &charlie_sled_db, + *TESTNET_BOOTSTRAP_TIMESTAMP, + *TESTNET_GENESIS_TIMESTAMP, + *TESTNET_GENESIS_HASH_BYTES, + charlie_wallet, + faucet_pubkeys.clone(), + false, + ) + .await?; + let money_contract_id = *MONEY_CONTRACT_ID; let alice_sled = alice_state.read().await.blockchain.sled_db.clone(); @@ -166,15 +183,18 @@ impl MoneyTestHarness { let faucet_merkle_tree = BridgeTree::::new(100); let alice_merkle_tree = BridgeTree::::new(100); let bob_merkle_tree = BridgeTree::::new(100); + let charlie_merkle_tree = BridgeTree::::new(100); Ok(Self { faucet_kp, alice_kp, bob_kp, + charlie_kp, faucet_pubkeys, faucet_state, alice_state, bob_state, + charlie_state, money_contract_id, proving_keys, mint_pk, @@ -184,6 +204,7 @@ impl MoneyTestHarness { faucet_merkle_tree, alice_merkle_tree, bob_merkle_tree, + charlie_merkle_tree, }) }