Przeglądaj źródła

dao-test: mint gov token

x 3 lat temu
rodzic
commit
e02daace77

+ 159 - 5
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(())
 }

+ 21 - 0
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<PublicKey>,
     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<MerkleNode, MERKLE_DEPTH>,
     pub alice_merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
     pub bob_merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    pub charlie_merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
 }
 
 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::<MerkleNode, MERKLE_DEPTH>::new(100);
         let alice_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
         let bob_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
+        let charlie_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::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,
         })
     }