Skip to content

Commit

Permalink
Merge pull request #2336 from dusk-network/moonlight-wallet-core
Browse files Browse the repository at this point in the history
wallet-core: add Moonlight stake operations
  • Loading branch information
Eduardo Leegwater Simões authored Sep 10, 2024
2 parents 2622c30 + e3e59a1 commit e385915
Show file tree
Hide file tree
Showing 6 changed files with 560 additions and 10 deletions.
4 changes: 4 additions & 0 deletions rusk/tests/config/stake.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ notes = [
10_000_000_000_000,
]

[[moonlight_account]]
address = "qe1FbZxf6YaCAeFNSvL1G82cBhG4Q4gBf4vKYo527Vws3b23jdbBuzKSFsdUHnZeBgsTnyNJLkApEpRyJw87sdzR9g9iESJrG5ZgpCs9jq88m6d4qMY5txGpaXskRQmkzE3"
balance = 10_000_000_000_000

[[stake]]
address = "qe1FbZxf6YaCAeFNSvL1G82cBhG4Q4gBf4vKYo527Vws3b23jdbBuzKSFsdUHnZeBgsTnyNJLkApEpRyJw87sdzR9g9iESJrG5ZgpCs9jq88m6d4qMY5txGpaXskRQmkzE3"
amount = 1_000_000_000_000
Expand Down
3 changes: 2 additions & 1 deletion rusk/tests/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
pub mod contract_deployment;
pub mod conversion;
pub mod gas_behavior;
pub mod moonlight_stake;
pub mod multi_transfer;
pub mod owner_calls;
pub mod stake;
pub mod phoenix_stake;
pub mod transfer;
pub mod unspendable;
240 changes: 240 additions & 0 deletions rusk/tests/services/moonlight_stake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use std::path::Path;
use std::sync::{Arc, RwLock};

use execution_core::stake::MINIMUM_STAKE;

use rand::prelude::*;
use rand::rngs::StdRng;
use rusk::{Result, Rusk};
use std::collections::HashMap;
use tempfile::tempdir;
use test_wallet::{self as wallet};
use tracing::info;

use crate::common::state::{generator_procedure, new_state};
use crate::common::wallet::{TestStateClient, TestStore};
use crate::common::*;

const BLOCK_HEIGHT: u64 = 1;
const BLOCK_GAS_LIMIT: u64 = 100_000_000_000;
const GAS_LIMIT: u64 = 10_000_000_000;
const GAS_PRICE: u64 = 1;

// Creates the Rusk initial state for the tests below
fn stake_state<P: AsRef<Path>>(dir: P) -> Result<Rusk> {
let snapshot = toml::from_str(include_str!("../config/stake.toml"))
.expect("Cannot deserialize config");

new_state(dir, &snapshot, BLOCK_GAS_LIMIT)
}

/// Stakes an amount Dusk and produces a block with this single transaction,
/// checking the stake is set successfully. It then proceeds to withdraw the
/// stake and checking it is correctly withdrawn.
fn wallet_stake(
rusk: &Rusk,
wallet: &wallet::Wallet<TestStore, TestStateClient>,
value: u64,
) {
let mut rng = StdRng::seed_from_u64(0xdead);

wallet
.get_stake(0)
.expect("stakeinfo to be found")
.amount
.expect("stake amount to be found");

assert!(
wallet
.get_stake(2)
.expect("stakeinfo to be found")
.amount
.is_none(),
"stake amount not to be found"
);

let tx = wallet
.moonlight_stake(0, 2, value, GAS_LIMIT, GAS_PRICE)
.expect("Failed to create a stake transaction");
let executed_txs = generator_procedure(
rusk,
&[tx],
BLOCK_HEIGHT,
BLOCK_GAS_LIMIT,
vec![],
None,
)
.expect("generator procedure to succeed");
if let Some(e) = &executed_txs
.first()
.expect("Transaction must be executed")
.err
{
panic!("Stake transaction failed due to {e}")
}

let stake = wallet.get_stake(2).expect("stake to be found");
let stake_value = stake.amount.expect("stake should have an amount").value;

assert_eq!(stake_value, value);

wallet
.get_stake(0)
.expect("stakeinfo to be found")
.amount
.expect("stake amount to be found");

let tx = wallet
.moonlight_unstake(&mut rng, 0, 0, GAS_LIMIT, GAS_PRICE)
.expect("Failed to unstake");
let spent_txs = generator_procedure(
rusk,
&[tx],
BLOCK_HEIGHT,
BLOCK_GAS_LIMIT,
vec![],
None,
)
.expect("generator procedure to succeed");
let spent_tx = spent_txs.first().expect("Unstake tx to be included");
assert_eq!(spent_tx.err, None, "unstake to be successfull");

let stake = wallet.get_stake(0).expect("stake should still be state");
assert_eq!(stake.amount, None);

let tx = wallet
.moonlight_stake_withdraw(&mut rng, 0, 1, GAS_LIMIT, GAS_PRICE)
.expect("failed to withdraw reward");
generator_procedure(
rusk,
&[tx],
BLOCK_HEIGHT,
BLOCK_GAS_LIMIT,
vec![],
None,
)
.expect("generator procedure to succeed");

let stake = wallet.get_stake(1).expect("stake should still be state");
assert_eq!(stake.reward, 0);
}

#[tokio::test(flavor = "multi_thread")]
pub async fn stake() -> Result<()> {
// Setup the logger
logger();

let tmp = tempdir().expect("Should be able to create temporary directory");
let rusk = stake_state(&tmp)?;

let cache = Arc::new(RwLock::new(HashMap::new()));

// Create a wallet
let wallet = wallet::Wallet::new(
TestStore,
TestStateClient {
rusk: rusk.clone(),
cache,
},
);

let original_root = rusk.state_root();

info!("Original Root: {:?}", hex::encode(original_root));

// Perform some staking actions.
wallet_stake(&rusk, &wallet, MINIMUM_STAKE);

// Check the state's root is changed from the original one
let new_root = rusk.state_root();
info!(
"New root after the 1st transfer: {:?}",
hex::encode(new_root)
);
assert_ne!(original_root, new_root, "Root should have changed");

// let recv = kadcast_recv.try_recv();
// let (_, _, h) = recv.expect("Transaction has not been locally
// propagated"); assert_eq!(h, 0, "Transaction locally propagated with
// wrong height");

Ok(())
}

/// Attempt to submit a management transaction intending it to fail. Verify that
/// the reward amount remains unchanged and confirm that the transaction indeed
/// fails
fn wallet_reward(
rusk: &Rusk,
wallet: &wallet::Wallet<TestStore, TestStateClient>,
) {
let mut rng = StdRng::seed_from_u64(0xdead);

let stake = wallet.get_stake(2).expect("stake to be found");
assert_eq!(stake.reward, 0, "stake reward must be empty");

let tx = wallet
.moonlight_stake_withdraw(&mut rng, 0, 2, GAS_LIMIT, GAS_PRICE)
.expect("Creating reward transaction should succeed");

let executed_txs = generator_procedure(
rusk,
&[tx],
BLOCK_HEIGHT,
BLOCK_GAS_LIMIT,
vec![],
None,
)
.expect("generator procedure to succeed");
let _ = executed_txs
.first()
.expect("Transaction must be executed")
.err
.as_ref()
.expect("reward transaction to fail");
let stake = wallet.get_stake(2).expect("stake to be found");
assert_eq!(stake.reward, 0, "stake reward must be empty");
}

#[tokio::test(flavor = "multi_thread")]
pub async fn reward() -> Result<()> {
// Setup the logger
logger();

let tmp = tempdir().expect("Should be able to create temporary directory");
let rusk = stake_state(&tmp)?;

let cache = Arc::new(RwLock::new(HashMap::new()));

// Create a wallet
let wallet = wallet::Wallet::new(
TestStore,
TestStateClient {
rusk: rusk.clone(),
cache,
},
);

let original_root = rusk.state_root();

info!("Original Root: {:?}", hex::encode(original_root));

// Perform some staking actions.
wallet_reward(&rusk, &wallet);

// Check the state's root is changed from the original one
let new_root = rusk.state_root();
info!(
"New root after the 1st transfer: {:?}",
hex::encode(new_root)
);
assert_ne!(original_root, new_root, "Root should have changed");

Ok(())
}
File renamed without changes.
Loading

0 comments on commit e385915

Please sign in to comment.