| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- //! Integration test of consensus staking and unstaking for Alice.
- //!
- //! We first airdrop Alice native tokes, and then she can stake,
- //! propose and unstake them a couple of times.
- //!
- //! With this test, we want to confirm the consensus contract state
- //! transitions work for a single party and are able to be verified.
- //!
- //! TODO: Malicious cases
- use darkfi::Result;
- use log::info;
- use darkfi_consensus_contract::model::{calculate_grace_period, EPOCH_LENGTH, REWARD};
- use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
- #[async_std::test]
- async fn consensus_contract_stake_unstake() -> Result<()> {
- init_logger();
- // Holders this test will use
- const HOLDERS: [Holder; 2] = [Holder::Faucet, Holder::Alice];
- // Some numbers we want to assert
- const ALICE_AIRDROP: u64 = 1000;
- // Slot to verify against
- let mut current_slot = 1;
- // Initialize harness
- let mut th = TestHarness::new(&["money".to_string(), "consensus".to_string()]).await?;
- // Now Alice can airdrop some native tokens to herself
- info!(target: "consensus", "[Faucet] =========================");
- info!(target: "consensus", "[Faucet] Building Alice airdrop tx");
- info!(target: "consensus", "[Faucet] =========================");
- let (airdrop_tx, airdrop_params) = th.airdrop_native(ALICE_AIRDROP, Holder::Alice)?;
- info!(target: "consensus", "[Faucet] ==========================");
- info!(target: "consensus", "[Faucet] Executing Alice airdrop tx");
- info!(target: "consensus", "[Faucet] ==========================");
- th.execute_airdrop_native_tx(Holder::Faucet, &airdrop_tx, &airdrop_params, current_slot)
- .await?;
- info!(target: "consensus", "[Alice] ==========================");
- info!(target: "consensus", "[Alice] Executing Alice airdrop tx");
- info!(target: "consensus", "[Alice] ==========================");
- th.execute_airdrop_native_tx(Holder::Alice, &airdrop_tx, &airdrop_params, current_slot).await?;
- th.assert_trees(&HOLDERS);
- // Gather new owncoin
- let alice_oc = th.gather_owncoin(Holder::Alice, airdrop_params.outputs[0].clone(), None)?;
- // Now Alice can stake her owncoin
- info!(target: "consensus", "[Alice] =================");
- info!(target: "consensus", "[Alice] Building stake tx");
- info!(target: "consensus", "[Alice] =================");
- let (stake_tx, stake_params, stake_secret_key) =
- th.stake(Holder::Alice, current_slot, alice_oc.clone()).await?;
- info!(target: "consensus", "[Faucet] ========================");
- info!(target: "consensus", "[Faucet] Executing Alice stake tx");
- info!(target: "consensus", "[Faucet] ========================");
- th.execute_stake_tx(Holder::Faucet, &stake_tx, &stake_params, current_slot).await?;
- info!(target: "consensus", "[Alice] ========================");
- info!(target: "consensus", "[Alice] Executing Alice stake tx");
- info!(target: "consensus", "[Alice] ========================");
- th.execute_stake_tx(Holder::Alice, &stake_tx, &stake_params, current_slot).await?;
- th.assert_trees(&HOLDERS);
- // Gather new staked owncoin
- let alice_staked_oc = th.gather_consensus_staked_owncoin(
- Holder::Alice,
- stake_params.output,
- Some(stake_secret_key),
- )?;
- // Verify values match
- assert!(alice_oc.note.value == alice_staked_oc.note.value);
- // We progress one slot
- current_slot += 1;
- let slot_checkpoint = th.generate_slot_checkpoint(current_slot).await?;
- // Since alice didn't wait for the grace period to pass, her proposal should fail
- info!(target: "consensus", "[Malicious] =====================================");
- info!(target: "consensus", "[Malicious] Checking proposal before grace period");
- info!(target: "consensus", "[Malicious] =====================================");
- let (proposal_tx, _, _, _) =
- th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
- th.execute_erroneous_proposal_txs(Holder::Alice, &vec![proposal_tx], current_slot, 1).await?;
- // We progress after grace period
- current_slot += (calculate_grace_period() * EPOCH_LENGTH) + EPOCH_LENGTH;
- let slot_checkpoint = th.generate_slot_checkpoint(current_slot).await?;
- // With alice's current coin value she can become the slot proposer,
- // so she creates a proposal transaction to burn her staked coin,
- // reward herself and mint the new coin.
- info!(target: "consensus", "[Alice] ====================");
- info!(target: "consensus", "[Alice] Building proposal tx");
- info!(target: "consensus", "[Alice] ====================");
- let (
- proposal_tx,
- proposal_params,
- _proposal_signing_secret_key,
- proposal_decryption_secret_key,
- ) = th.proposal(Holder::Alice, slot_checkpoint, alice_staked_oc.clone()).await?;
- info!(target: "consensus", "[Faucet] ===========================");
- info!(target: "consensus", "[Faucet] Executing Alice proposal tx");
- info!(target: "consensus", "[Faucet] ===========================");
- th.execute_proposal_tx(Holder::Faucet, &proposal_tx, &proposal_params, current_slot).await?;
- info!(target: "consensus", "[Alice] ===========================");
- info!(target: "consensus", "[Alice] Executing Alice proposal tx");
- info!(target: "consensus", "[Alice] ===========================");
- th.execute_proposal_tx(Holder::Alice, &proposal_tx, &proposal_params, current_slot).await?;
- th.assert_trees(&HOLDERS);
- // Gather new staked owncoin which includes the reward
- let alice_rewarded_staked_oc = th.gather_consensus_staked_owncoin(
- Holder::Alice,
- proposal_params.output,
- Some(proposal_decryption_secret_key),
- )?;
- // Verify values match
- assert!((alice_staked_oc.note.value + REWARD) == alice_rewarded_staked_oc.note.value);
- // We progress one slot
- current_slot += 1;
- th.generate_slot_checkpoint(current_slot).await?;
- // Alice can request for her owncoin to get unstaked
- info!(target: "consensus", "[Alice] ===========================");
- info!(target: "consensus", "[Alice] Building unstake request tx");
- info!(target: "consensus", "[Alice] ===========================");
- let (
- unstake_request_tx,
- unstake_request_params,
- unstake_request_output_secret_key,
- _unstake_request_signature_secret_key,
- ) = th.unstake_request(Holder::Alice, current_slot, alice_rewarded_staked_oc.clone()).await?;
- info!(target: "consensus", "[Faucet] ==================================");
- info!(target: "consensus", "[Faucet] Executing Alice unstake request tx");
- info!(target: "consensus", "[Faucet] ==================================");
- th.execute_unstake_request_tx(
- Holder::Faucet,
- &unstake_request_tx,
- &unstake_request_params,
- current_slot,
- )
- .await?;
- info!(target: "consensus", "[Alice] ==================================");
- info!(target: "consensus", "[Alice] Executing Alice unstake request tx");
- info!(target: "consensus", "[Alice] ==================================");
- th.execute_unstake_request_tx(
- Holder::Alice,
- &unstake_request_tx,
- &unstake_request_params,
- current_slot,
- )
- .await?;
- th.assert_trees(&HOLDERS);
- // Gather new unstake request owncoin
- let alice_unstake_request_oc = th.gather_consensus_unstaked_owncoin(
- Holder::Alice,
- unstake_request_params.output,
- Some(unstake_request_output_secret_key),
- )?;
- // Verify values match
- assert!(alice_rewarded_staked_oc.note.value == alice_unstake_request_oc.note.value);
- // Now we will test if we can reuse token in proposal or unstake it again
- current_slot += 1;
- let slot_checkpoint = th.generate_slot_checkpoint(current_slot).await?;
- info!(target: "consensus", "[Malicious] ========================================");
- info!(target: "consensus", "[Malicious] Checking using unstaked coin in proposal");
- info!(target: "consensus", "[Malicious] ========================================");
- let (proposal_tx, _, _, _) =
- th.proposal(Holder::Alice, slot_checkpoint, alice_unstake_request_oc.clone()).await?;
- th.execute_erroneous_proposal_txs(Holder::Alice, &vec![proposal_tx], current_slot, 1).await?;
- info!(target: "consensus", "[Malicious] =============================");
- info!(target: "consensus", "[Malicious] Checking unstaking coin again");
- info!(target: "consensus", "[Malicious] =============================");
- let (unstake_request_tx, _, _, _) =
- th.unstake_request(Holder::Alice, current_slot, alice_unstake_request_oc.clone()).await?;
- th.execute_erroneous_unstake_request_txs(
- Holder::Alice,
- &vec![unstake_request_tx],
- current_slot,
- 1,
- )
- .await?;
- // We progress after grace period
- current_slot += (calculate_grace_period() * EPOCH_LENGTH) + EPOCH_LENGTH;
- // Now Alice can unstake her owncoin
- info!(target: "consensus", "[Alice] ===================");
- info!(target: "consensus", "[Alice] Building unstake tx");
- info!(target: "consensus", "[Alice] ===================");
- let (unstake_tx, unstake_params, _) =
- th.unstake(Holder::Alice, alice_unstake_request_oc.clone())?;
- info!(target: "consensus", "[Faucet] ==========================");
- info!(target: "consensus", "[Faucet] Executing Alice unstake tx");
- info!(target: "consensus", "[Faucet] ==========================");
- th.execute_unstake_tx(Holder::Faucet, &unstake_tx, &unstake_params, current_slot).await?;
- info!(target: "consensus", "[Alice] ==========================");
- info!(target: "consensus", "[Alice] Executing Alice unstake tx");
- info!(target: "consensus", "[Alice] ==========================");
- th.execute_unstake_tx(Holder::Alice, &unstake_tx, &unstake_params, current_slot).await?;
- th.assert_trees(&HOLDERS);
- // Gather new unstaked owncoin
- let alice_unstaked_oc = th.gather_owncoin(Holder::Alice, unstake_params.output, None)?;
- // Verify values match
- assert!(alice_unstake_request_oc.note.value == alice_unstaked_oc.note.value);
- // Statistics
- th.statistics();
- // Thanks for reading
- Ok(())
- }
|