use crate::{
block::Block,
blockchain::GENESIS_PERIOD,
crypto::{hash, SaitoHash},
golden_ticket::GoldenTicket,
slip::{Slip, SlipType},
transaction::TransactionType,
};
use bigint::uint::U256;
use log::{info, trace};
#[derive(Debug, Clone)]
pub struct Staking {
pub deposits: Vec<Slip>,
pub stakers: Vec<Slip>,
pub pending: Vec<Slip>,
}
impl Staking {
pub fn new() -> Staking {
Staking {
deposits: vec![],
stakers: vec![],
pending: vec![],
}
}
pub fn find_winning_staker(&self, random_number: SaitoHash) -> Option<Slip> {
if self.stakers.is_empty() {
return None;
}
let x = U256::from_big_endian(&random_number);
let y = self.stakers.len();
let z = U256::from_big_endian(&y.to_be_bytes());
let (zy, _bolres) = x.overflowing_rem(z);
let retrieve_from_pos = zy.low_u64();
let winning_slip = self.stakers[retrieve_from_pos as usize].clone();
Some(winning_slip)
}
pub fn reset_staker_table(
&mut self,
staking_treasury: u64,
) -> (Vec<Slip>, Vec<Slip>, Vec<Slip>) {
info!("===========================");
info!("=== RESET STAKING TABLE ===");
info!("===========================");
let res_spend: Vec<Slip> = vec![];
let res_unspend: Vec<Slip> = vec![];
let res_delete: Vec<Slip> = vec![];
for i in 0..self.pending.len() {
self.add_staker(self.pending[i].clone());
}
for i in 0..self.deposits.len() {
self.add_staker(self.deposits[i].clone());
}
self.pending = vec![];
self.deposits = vec![];
if self.stakers.is_empty() {
return (res_spend, res_unspend, res_delete);
}
let staking_payout_per_block: u64 = staking_treasury / GENESIS_PERIOD;
let mut total_staked: u64 = 0;
for i in 0..self.stakers.len() {
total_staked += self.stakers[i].get_amount();
}
let average_staked = total_staked / self.stakers.len() as u64;
let m = U256::from_big_endian(&staking_payout_per_block.to_be_bytes());
let p = U256::from_big_endian(&self.stakers.len().to_be_bytes());
let (q, _r) = m.overflowing_div(p);
let average_staker_payout = q.as_u64();
for i in 0..self.stakers.len() {
let my_staked_amount = self.stakers[i].get_amount();
let a = U256::from_big_endian(&my_staked_amount.to_be_bytes());
let b = U256::from_big_endian(&average_staker_payout.to_be_bytes());
let nominator: U256 = a.saturating_mul(b);
let denominator = U256::from_big_endian(&average_staked.to_be_bytes());
let (z, f) = nominator.overflowing_div(denominator);
let mut staking_profit: u64 = 0;
if !f {
staking_profit = z.as_u64();
}
self.stakers[i].set_payout(staking_profit);
}
(res_spend, res_unspend, res_delete)
}
pub fn validate_slip_in_deposits(&self, slip: Slip) -> bool {
for i in 0..self.deposits.len() {
if slip.get_utxoset_key() == self.deposits[i].get_utxoset_key() {
return true;
}
}
false
}
pub fn validate_slip_in_stakers(&self, slip: Slip) -> bool {
for i in 0..self.stakers.len() {
if slip.get_utxoset_key() == self.stakers[i].get_utxoset_key() {
return true;
}
}
false
}
pub fn validate_slip_in_pending(&self, slip: Slip) -> bool {
for i in 0..self.pending.len() {
if slip.get_utxoset_key() == self.pending[i].get_utxoset_key() {
return true;
}
}
false
}
pub fn add_deposit(&mut self, slip: Slip) {
self.deposits.push(slip);
}
pub fn add_staker(&mut self, slip: Slip) -> bool {
if self.stakers.len() == 0 {
self.stakers.push(slip);
return true;
} else {
for i in 0..self.stakers.len() {
let how_compares = slip.compare(self.stakers[i].clone());
if how_compares == 2 {
if self.stakers.len() == (i + 1) {
self.stakers.push(slip);
return true;
}
} else {
if how_compares == 1 {
self.stakers.insert(i, slip);
return true;
}
if how_compares == 3 {
return false;
}
}
}
self.stakers.push(slip);
return true;
}
}
pub fn add_pending(&mut self, slip: Slip) {
self.pending.push(slip);
}
pub fn remove_deposit(&mut self, slip: Slip) -> bool {
for i in 0..self.deposits.len() {
if slip.get_utxoset_key() == self.deposits[i].get_utxoset_key() {
let _removed_slip = self.deposits.remove(i);
return true;
}
}
false
}
pub fn remove_staker(&mut self, slip: Slip) -> bool {
for i in 0..self.stakers.len() {
if slip.get_utxoset_key() == self.stakers[i].get_utxoset_key() {
let _removed_slip = self.stakers.remove(i);
return true;
}
}
false
}
pub fn remove_pending(&mut self, slip: Slip) -> bool {
for i in 0..self.pending.len() {
if slip.get_utxoset_key() == self.pending[i].get_utxoset_key() {
let _removed_slip = self.pending.remove(i);
return true;
}
}
false
}
pub fn on_chain_reorganization(
&mut self,
block: &Block,
longest_chain: bool,
) -> (Vec<Slip>, Vec<Slip>, Vec<Slip>) {
let res_spend: Vec<Slip> = vec![];
let res_unspend: Vec<Slip> = vec![];
let res_delete: Vec<Slip> = vec![];
for tx in block.get_transactions() {
if tx.get_transaction_type() == TransactionType::StakerWithdrawal {
if longest_chain {
if tx.inputs[0].get_slip_type() == SlipType::StakerWithdrawalPending {
self.remove_pending(tx.inputs[0].clone());
}
if tx.inputs[0].get_slip_type() == SlipType::StakerWithdrawalStaking {
self.remove_staker(tx.inputs[0].clone());
}
} else {
if tx.inputs[0].get_slip_type() == SlipType::StakerWithdrawalPending {
self.add_pending(tx.inputs[0].clone());
}
if tx.inputs[0].get_slip_type() == SlipType::StakerWithdrawalStaking {
self.add_staker(tx.inputs[0].clone());
}
}
}
if tx.get_transaction_type() == TransactionType::StakerDeposit {
for i in 0..tx.outputs.len() {
if tx.outputs[i].get_slip_type() == SlipType::StakerDeposit {
if longest_chain {
self.add_deposit(tx.outputs[i].clone());
} else {
self.remove_deposit(tx.outputs[i].clone());
}
}
}
}
}
if longest_chain {
if self.stakers.is_empty() {
let (_res_spend, _res_unspend, _res_delete) =
self.reset_staker_table(block.get_staking_treasury());
}
} else {
if self.pending.is_empty() {
self.pending = vec![];
self.deposits = vec![];
for i in 0..self.stakers.len() {
if self.stakers[i].get_slip_type() == SlipType::StakerOutput {
self.pending.push(self.stakers[i].clone());
}
if self.stakers[i].get_slip_type() == SlipType::StakerDeposit {
self.deposits.push(self.stakers[i].clone());
}
}
self.stakers = vec![];
}
}
if block.get_has_fee_transaction() && block.get_has_golden_ticket() {
let fee_transaction =
&block.get_transactions()[block.get_fee_transaction_idx() as usize];
let golden_ticket_transaction =
&block.get_transactions()[block.get_golden_ticket_idx() as usize];
let golden_ticket: GoldenTicket = GoldenTicket::deserialize_for_transaction(
golden_ticket_transaction.get_message().to_vec(),
);
let mut next_random_number = hash(&golden_ticket.get_random().to_vec());
next_random_number = hash(&next_random_number.to_vec());
next_random_number = hash(&next_random_number.to_vec());
let mut is_there_a_staker_output = false;
for i in 0..fee_transaction.outputs.len() {
if fee_transaction.outputs[i].get_slip_type() == SlipType::StakerOutput {
is_there_a_staker_output = true;
}
}
if is_there_a_staker_output == false {
return (res_spend, res_unspend, res_delete);
}
if fee_transaction.inputs.is_empty() {
return (res_spend, res_unspend, res_delete);
}
if longest_chain {
trace!(
"Rolling forward and moving into pending: {}!",
self.stakers.len()
);
if self.stakers.is_empty() {
let (_res_spend, _res_unspend, _res_delete) =
self.reset_staker_table(block.get_staking_treasury());
}
let mut slips_to_remove_from_staking = vec![];
let mut slips_to_add_to_pending = vec![];
let mut staker_slip_num = 0;
for i in 0..fee_transaction.outputs.len() {
let staker_output = fee_transaction.outputs[i].clone();
if fee_transaction.inputs.len() <= staker_slip_num {
break;
}
if staker_output.get_slip_type() == SlipType::StakerOutput {
next_random_number = hash(&next_random_number.to_vec()); next_random_number = hash(&next_random_number.to_vec());
let lucky_staker_option = self.find_winning_staker(next_random_number);
if let Some(lucky_staker) = lucky_staker_option {
info!("the lucky staker is: {:?}", lucky_staker);
trace!(
"moving from staker into pending: {}",
lucky_staker.get_amount()
);
slips_to_remove_from_staking.push(lucky_staker.clone());
slips_to_add_to_pending.push(staker_output.clone());
}
staker_slip_num += 1;
next_random_number = hash(&next_random_number.to_vec());
}
}
for i in 0..slips_to_remove_from_staking.len() {
if self.remove_staker(slips_to_remove_from_staking[i].clone()) == true {
self.add_pending(slips_to_add_to_pending[i].clone());
}
}
if self.stakers.is_empty() {
let (_res_spend, _res_unspend, _res_delete) =
self.reset_staker_table(block.get_staking_treasury());
}
} else {
info!("roll backward...");
if self.pending.is_empty() {
self.pending = vec![];
self.deposits = vec![];
for i in 0..self.stakers.len() {
if self.stakers[i].get_slip_type() == SlipType::StakerOutput {
self.pending.push(self.stakers[i].clone());
}
if self.stakers[i].get_slip_type() == SlipType::StakerDeposit {
self.deposits.push(self.stakers[i].clone());
}
}
self.stakers = vec![];
}
let mut staker_slip_num = 0;
for i in 0..fee_transaction.outputs.len() {
let staker_output = fee_transaction.outputs[i].clone();
if fee_transaction.inputs.len() < staker_slip_num {
break;
}
let staker_input = fee_transaction.inputs[staker_slip_num].clone();
if staker_output.get_slip_type() == SlipType::StakerOutput {
self.remove_pending(staker_output.clone());
let slip_type = staker_input.get_slip_type();
if slip_type == SlipType::StakerDeposit {
self.add_deposit(staker_input.clone());
}
if slip_type == SlipType::StakerOutput {
self.add_staker(staker_input.clone());
}
staker_slip_num += 1;
}
}
if self.pending.is_empty() {
self.pending = vec![];
self.deposits = vec![];
for i in 0..self.stakers.len() {
if self.stakers[i].get_slip_type() == SlipType::StakerOutput {
self.pending.push(self.stakers[i].clone());
}
if self.stakers[i].get_slip_type() == SlipType::StakerDeposit {
self.deposits.push(self.stakers[i].clone());
}
}
self.stakers = vec![];
}
}
}
(res_spend, res_unspend, res_delete)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utilities::test_manager::TestManager;
use crate::transaction::Transaction;
use crate::{
blockchain::Blockchain,
slip::{Slip, SlipType},
time::create_timestamp,
wallet::Wallet,
};
use log::info;
use std::sync::Arc;
use tokio::sync::RwLock;
#[test]
fn staking_add_staker_slips_in_different_order_and_check_sorting_works() {
let mut staking1 = Staking::new();
let mut staking2 = Staking::new();
let mut slip1 = Slip::new();
slip1.set_amount(1);
slip1.set_slip_type(SlipType::StakerDeposit);
let mut slip2 = Slip::new();
slip2.set_amount(2);
slip2.set_slip_type(SlipType::StakerDeposit);
let mut slip3 = Slip::new();
slip3.set_amount(3);
slip3.set_slip_type(SlipType::StakerDeposit);
let mut slip4 = Slip::new();
slip4.set_amount(4);
slip4.set_slip_type(SlipType::StakerDeposit);
let mut slip5 = Slip::new();
slip5.set_amount(5);
slip5.set_slip_type(SlipType::StakerDeposit);
staking1.add_staker(slip1.clone());
assert_eq!(staking1.stakers.len(), 1);
staking1.add_staker(slip2.clone());
assert_eq!(staking1.stakers.len(), 2);
staking1.add_staker(slip3.clone());
assert_eq!(staking1.stakers.len(), 3);
staking1.add_staker(slip4.clone());
assert_eq!(staking1.stakers.len(), 4);
staking1.add_staker(slip5.clone());
assert_eq!(staking1.stakers.len(), 5);
staking2.add_staker(slip2.clone());
staking2.add_staker(slip4.clone());
staking2.add_staker(slip1.clone());
staking2.add_staker(slip5.clone());
staking2.add_staker(slip3.clone());
staking2.add_staker(slip2.clone());
staking2.add_staker(slip1.clone());
assert_eq!(staking1.stakers.len(), 5);
assert_eq!(staking2.stakers.len(), 5);
for i in 0..staking2.stakers.len() {
info!(
"{} -- {}",
staking1.stakers[i].get_amount(),
staking2.stakers[i].get_amount()
);
}
for i in 0..staking2.stakers.len() {
assert_eq!(
staking1.stakers[i].clone().serialize_for_net(),
staking2.stakers[i].clone().serialize_for_net()
);
assert_eq!(
staking1.stakers[i]
.clone()
.compare(staking2.stakers[i].clone()),
3
); }
}
#[test]
fn staking_add_deposit_slip_and_payout_calculation_test() {
let mut staking = Staking::new();
let mut slip1 = Slip::new();
slip1.set_amount(200_000_000);
slip1.set_slip_type(SlipType::StakerDeposit);
let mut slip2 = Slip::new();
slip2.set_amount(300_000_000);
slip2.set_slip_type(SlipType::StakerDeposit);
let mut slip3 = Slip::new();
slip3.set_amount(400_000_000);
slip3.set_slip_type(SlipType::StakerDeposit);
let mut slip4 = Slip::new();
slip4.set_amount(500_000_000);
slip4.set_slip_type(SlipType::StakerDeposit);
let mut slip5 = Slip::new();
slip5.set_amount(600_000_000);
slip5.set_slip_type(SlipType::StakerDeposit);
staking.add_deposit(slip1);
staking.add_deposit(slip2);
staking.add_deposit(slip3);
staking.add_deposit(slip4);
staking.add_deposit(slip5);
let (_res_spend, _res_unspend, _res_delete) = staking.reset_staker_table(1_000_000_000);
assert_eq!(
staking.stakers[4].get_amount() + staking.stakers[4].get_payout(),
210000000
);
assert_eq!(
staking.stakers[3].get_amount() + staking.stakers[3].get_payout(),
315000000
);
assert_eq!(
staking.stakers[2].get_amount() + staking.stakers[2].get_payout(),
420000000
);
assert_eq!(
staking.stakers[1].get_amount() + staking.stakers[1].get_payout(),
525000000
);
assert_eq!(
staking.stakers[0].get_amount() + staking.stakers[0].get_payout(),
630000000
);
}
#[test]
fn staking_remove_staker_code_handles_duplicates_properly() {
let mut staking = Staking::new();
let mut slip1 = Slip::new();
slip1.set_amount(200_000_000);
slip1.set_slip_type(SlipType::StakerDeposit);
let mut slip2 = Slip::new();
slip2.set_amount(300_000_000);
slip2.set_slip_type(SlipType::StakerDeposit);
let mut slip3 = Slip::new();
slip3.set_amount(400_000_000);
slip3.set_slip_type(SlipType::StakerDeposit);
let mut slip4 = Slip::new();
slip4.set_amount(500_000_000);
slip4.set_slip_type(SlipType::StakerDeposit);
let mut slip5 = Slip::new();
slip5.set_amount(600_000_000);
slip5.set_slip_type(SlipType::StakerDeposit);
staking.add_deposit(slip1.clone());
staking.add_deposit(slip2.clone());
staking.add_deposit(slip3.clone());
staking.add_deposit(slip4.clone());
staking.add_deposit(slip5.clone());
let (_res_spend, _res_unspend, _res_delete) = staking.reset_staker_table(1_000_000_000);
assert_eq!(staking.stakers.len(), 5);
assert_eq!(staking.remove_staker(slip1.clone()), true);
assert_eq!(staking.remove_staker(slip2.clone()), true);
assert_eq!(staking.remove_staker(slip1.clone()), false);
assert_eq!(staking.stakers.len(), 3);
assert_eq!(staking.remove_staker(slip5.clone()), true);
assert_eq!(staking.remove_staker(slip5.clone()), false);
assert_eq!(staking.stakers.len(), 2);
}
#[tokio::test]
#[serial_test::serial]
async fn staking_create_blockchain_with_two_staking_deposits_one_staker_payout_per_block() {
let wallet_lock = Arc::new(RwLock::new(Wallet::new()));
let blockchain_lock = Arc::new(RwLock::new(Blockchain::new(wallet_lock.clone())));
let mut test_manager = TestManager::new(blockchain_lock.clone(), wallet_lock.clone());
{
let mut blockchain = blockchain_lock.write().await;
let wallet = wallet_lock.read().await;
let publickey = wallet.get_publickey();
let mut slip1 = Slip::new();
slip1.set_amount(200_000_000);
slip1.set_slip_type(SlipType::StakerDeposit);
let mut slip2 = Slip::new();
slip2.set_amount(300_000_000);
slip2.set_slip_type(SlipType::StakerDeposit);
slip1.set_publickey(publickey);
slip2.set_publickey(publickey);
slip1.generate_utxoset_key();
slip2.generate_utxoset_key();
slip1.on_chain_reorganization(&mut blockchain.utxoset, true, 1);
slip2.on_chain_reorganization(&mut blockchain.utxoset, true, 1);
blockchain.staking.add_deposit(slip1);
blockchain.staking.add_deposit(slip2);
blockchain.staking.reset_staker_table(1_000_000_000); }
let current_timestamp = create_timestamp();
let block1 = test_manager
.generate_block_and_metadata([0; 32], current_timestamp, 3, 0, false, vec![])
.await;
let block1_hash = block1.get_hash();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block1).await;
let block2 = test_manager
.generate_block_and_metadata(
block1_hash,
current_timestamp + 120000,
0,
1,
false,
vec![],
)
.await;
let block2_hash = block2.get_hash();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block2).await;
{
let blockchain = blockchain_lock.write().await;
assert_eq!(blockchain.staking.stakers.len(), 2);
assert_eq!(blockchain.staking.pending.len(), 0);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
let block3 = test_manager
.generate_block_and_metadata(
block2_hash,
current_timestamp + 240000,
0,
1,
true,
vec![],
)
.await;
let block3_hash = block3.get_hash();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block3).await;
{
let blockchain = blockchain_lock.write().await;
assert_eq!(blockchain.staking.stakers.len(), 1);
assert_eq!(blockchain.staking.pending.len(), 1);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
let block4 = test_manager
.generate_block_and_metadata(
block3_hash,
current_timestamp + 360000,
0,
1,
false,
vec![],
)
.await;
let block4_hash = block4.get_hash();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block4).await;
{
let blockchain = blockchain_lock.write().await;
assert_eq!(blockchain.staking.stakers.len(), 1);
assert_eq!(blockchain.staking.pending.len(), 1);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
test_manager.set_latest_block_hash(block4_hash);
test_manager
.add_block(current_timestamp + 480000, 0, 1, true, vec![])
.await;
{
let blockchain = blockchain_lock.read().await;
assert_eq!(blockchain.staking.stakers.len(), 2);
assert_eq!(blockchain.staking.pending.len(), 0);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
test_manager
.add_block(current_timestamp + 600000, 0, 1, false, vec![])
.await;
{
let blockchain = blockchain_lock.read().await;
assert_eq!(blockchain.staking.stakers.len(), 2);
assert_eq!(blockchain.staking.pending.len(), 0);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
test_manager
.add_block(current_timestamp + 720000, 0, 1, true, vec![])
.await;
{
let blockchain = blockchain_lock.read().await;
assert_eq!(blockchain.staking.stakers.len(), 1);
assert_eq!(blockchain.staking.pending.len(), 1);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
test_manager
.add_block(current_timestamp + 840000, 0, 1, false, vec![])
.await;
{
let blockchain = blockchain_lock.read().await;
assert_eq!(blockchain.staking.stakers.len(), 1);
assert_eq!(blockchain.staking.pending.len(), 1);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
test_manager
.add_block(current_timestamp + 960000, 0, 1, true, vec![])
.await;
{
let blockchain = blockchain_lock.read().await;
assert_eq!(blockchain.staking.stakers.len(), 2);
assert_eq!(blockchain.staking.pending.len(), 0);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
}
#[tokio::test]
#[serial_test::serial]
async fn staking_create_blockchain_with_many_staking_deposits_many_staker_payouts_per_block() {
let wallet_lock = Arc::new(RwLock::new(Wallet::new()));
let blockchain_lock = Arc::new(RwLock::new(Blockchain::new(wallet_lock.clone())));
let mut test_manager = TestManager::new(blockchain_lock.clone(), wallet_lock.clone());
{
let mut blockchain = blockchain_lock.write().await;
let wallet = wallet_lock.read().await;
let publickey = wallet.get_publickey();
let mut slip1 = Slip::new();
slip1.set_amount(200_000_000);
slip1.set_slip_type(SlipType::StakerDeposit);
let mut slip2 = Slip::new();
slip2.set_amount(300_000_000);
slip2.set_slip_type(SlipType::StakerDeposit);
let mut slip3 = Slip::new();
slip3.set_amount(400_000_000);
slip3.set_slip_type(SlipType::StakerDeposit);
slip1.set_publickey(publickey);
slip2.set_publickey(publickey);
slip3.set_publickey(publickey);
slip1.generate_utxoset_key();
slip2.generate_utxoset_key();
slip3.generate_utxoset_key();
slip1.on_chain_reorganization(&mut blockchain.utxoset, true, 1);
slip2.on_chain_reorganization(&mut blockchain.utxoset, true, 1);
slip3.on_chain_reorganization(&mut blockchain.utxoset, true, 1);
blockchain.staking.add_deposit(slip1);
blockchain.staking.add_deposit(slip2);
blockchain.staking.add_deposit(slip3);
blockchain.staking.reset_staker_table(1_000_000_000); }
let current_timestamp = create_timestamp();
let block1 = test_manager
.generate_block_and_metadata([0; 32], current_timestamp, 10, 0, false, vec![])
.await;
let block1_hash = block1.get_hash();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block1).await;
test_manager.set_latest_block_hash(block1_hash);
test_manager
.add_block(current_timestamp + 120000, 0, 1, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 240000, 0, 1, false, vec![])
.await;
{
let blockchain = blockchain_lock.write().await;
assert_eq!(blockchain.staking.stakers.len(), 3);
assert_eq!(blockchain.staking.pending.len(), 0);
assert_eq!(blockchain.staking.deposits.len(), 0);
}
test_manager
.add_block(current_timestamp + 360000, 0, 1, true, vec![])
.await;
test_manager
.add_block(current_timestamp + 480000, 0, 1, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 600000, 0, 1, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 720000, 0, 1, true, vec![])
.await;
test_manager
.add_block(current_timestamp + 840000, 0, 1, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 960000, 0, 1, true, vec![])
.await;
{
let blockchain = blockchain_lock.read().await;
assert_eq!(blockchain.get_latest_block_id(), 9);
assert_eq!(
3,
blockchain.staking.stakers.len()
+ blockchain.staking.pending.len()
+ blockchain.staking.deposits.len()
);
}
}
#[tokio::test]
#[serial_test::serial]
async fn blockchain_staking_deposits_test() {
let wallet_lock = Arc::new(RwLock::new(Wallet::new()));
let blockchain_lock = Arc::new(RwLock::new(Blockchain::new(wallet_lock.clone())));
let mut test_manager = TestManager::new(blockchain_lock.clone(), wallet_lock.clone());
let current_timestamp = create_timestamp();
let publickey;
{
let wallet = wallet_lock.read().await;
publickey = wallet.get_publickey();
}
test_manager
.add_block(current_timestamp, 10, 0, false, vec![])
.await;
let block1_hash;
{
let blockchain = blockchain_lock.read().await;
block1_hash = blockchain.get_latest_block_hash();
}
let mut stx1: Transaction;
let mut stx2: Transaction;
{
let mut wallet = wallet_lock.write().await;
stx1 = wallet.create_staking_deposit_transaction(100000).await;
stx2 = wallet.create_staking_deposit_transaction(200000).await;
stx1.generate_metadata(publickey);
stx2.generate_metadata(publickey);
}
let mut transactions: Vec<Transaction> = vec![];
transactions.push(stx1);
transactions.push(stx2);
let mut block2 = Block::generate(
&mut transactions,
block1_hash,
wallet_lock.clone(),
blockchain_lock.clone(),
current_timestamp + 120000,
)
.await;
block2.generate_metadata();
let block2_hash = block2.get_hash();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block2).await;
info!("- AFTER BLOCK 2 - deposit");
test_manager.set_latest_block_hash(block2_hash);
test_manager
.add_block(current_timestamp + 240000, 0, 1, true, vec![])
.await;
info!("- AFTER BLOCK 3 - PAYOUT has happened -");
{
let blockchain = blockchain_lock.write().await;
info!("STAKERS {:?}", blockchain.staking.stakers);
info!("PENDING {:?}", blockchain.staking.pending);
info!("DEPOSITS {:?}", blockchain.staking.deposits);
}
test_manager
.add_block(current_timestamp + 360000, 0, 1, false, vec![])
.await;
{
let blockchain = blockchain_lock.write().await;
info!("AFTER BLOCK #4");
info!("STAKERS {:?}", blockchain.staking.stakers);
info!("PENDING {:?}", blockchain.staking.pending);
info!("DEPOSITS {:?}", blockchain.staking.deposits);
}
test_manager
.add_block(current_timestamp + 480000, 0, 1, true, vec![])
.await;
let block5_hash;
{
let blockchain = blockchain_lock.read().await;
block5_hash = blockchain.get_latest_block_hash();
}
let mut wstx1: Transaction;
{
let mut wallet = wallet_lock.write().await;
let blockchain = blockchain_lock.write().await;
wstx1 = wallet
.create_staking_withdrawal_transaction(&blockchain.staking)
.await;
wstx1.generate_metadata(publickey);
}
let mut transactions: Vec<Transaction> = vec![];
info!("----------");
info!("- WITHDRAW STAKER SLIP --");
info!("----------");
info!("---{:?}---", wstx1);
info!("----------");
transactions.push(wstx1);
let mut block6 = Block::generate(
&mut transactions,
block5_hash,
wallet_lock.clone(),
blockchain_lock.clone(),
current_timestamp + 600000,
)
.await;
block6.generate_metadata();
let block6_id = block6.get_id();
Blockchain::add_block_to_blockchain(blockchain_lock.clone(), block6).await;
{
let blockchain = blockchain_lock.read().await;
blockchain.print();
info!(
"LATESTID: {} / {}",
block6_id,
blockchain.get_latest_block_id()
);
assert_eq!(blockchain.get_latest_block_id(), 6);
assert_eq!(
blockchain.staking.stakers.len() + blockchain.staking.pending.len(),
1
);
}
}
#[tokio::test]
#[serial_test::serial]
async fn blockchain_roll_forward_staking_table_test_with_test_manager() {
let wallet_lock = Arc::new(RwLock::new(Wallet::new()));
let blockchain_lock = Arc::new(RwLock::new(Blockchain::new(wallet_lock.clone())));
let mut test_manager = TestManager::new(blockchain_lock.clone(), wallet_lock.clone());
let publickey;
let current_timestamp = create_timestamp();
{
let wallet = wallet_lock.read().await;
publickey = wallet.get_publickey();
info!("publickey: {:?}", publickey);
}
test_manager
.add_block(current_timestamp, 3, 0, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 120000, 0, 1, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 240000, 0, 1, false, vec![])
.await;
test_manager
.add_block(current_timestamp + 360000, 0, 1, true, vec![])
.await;
test_manager.check_utxoset().await;
test_manager.check_token_supply().await;
}
}