Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing add stake operation #3196

Draft
wants to merge 3 commits into
base: albatross
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions primitives/account/src/account/staking_contract/receipts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,22 @@ pub struct DeleteValidatorReceipt {
}
convert_receipt!(DeleteValidatorReceipt);

#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub enum BalanceType {
Active,
Inactive,
Retired,
}

/// Receipt for most staker-related transactions. This is necessary to be able to revert
/// these transactions.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct AddStakeReceipt {
/// the balance which the stake was attributed to.
pub credited_balance: BalanceType,
}
convert_receipt!(AddStakeReceipt);

/// Receipt for most staker-related transactions. This is necessary to be able to revert
/// these transactions.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
Expand Down
78 changes: 59 additions & 19 deletions primitives/account/src/account/staking_contract/staker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use nimiq_primitives::coin::Coin;
use nimiq_primitives::policy::Policy;
use serde::{Deserialize, Serialize};

use super::AddStakeReceipt;
use crate::BalanceType;
#[cfg(feature = "interaction-traits")]
use crate::{
account::staking_contract::{
Expand Down Expand Up @@ -283,41 +285,65 @@ impl StakingContract {
staker_address: &Address,
value: Coin,
tx_logger: &mut TransactionLog,
) -> Result<(), AccountError> {
) -> Result<AddStakeReceipt, AccountError> {
// Get the staker.
let mut staker = store.expect_staker(staker_address)?;

// Fail if the minimum stake would be violated for the non-retired funds (invariant 1).
Staker::enforce_min_stake(
staker.active_balance + value,
staker.inactive_balance,
staker.retired_balance,
)?;
// Add stake txs never violate minimum stake for the non-retired funds (invariant 1),
// because the intrinsic tx checks that value is >= min stake.
assert!(
Staker::enforce_min_stake(
staker.active_balance + value,
staker.inactive_balance,
staker.retired_balance,
)
.is_ok(),
"Add stake should never violate the min stake invariants"
);

// All checks passed, not allowed to fail from here on!

// If we are delegating to a validator, we need to update it.
if let Some(validator_address) = &staker.delegation {
// Check that the delegation is still valid, i.e. the validator hasn't been deleted.
store.expect_validator(validator_address)?;
self.increase_stake_to_validator(store, validator_address, value);
}

// Update the staker's and staking contract's balances.
staker.active_balance += value;
// We want to preferentially credit the active balance. Only if there is no active balance,
// then we will choose the balance with the most funds between inactive and retired.
let credited_balance = if !staker.active_balance.is_zero() {
staker.active_balance += value;
BalanceType::Active
} else if staker.inactive_balance >= staker.retired_balance {
staker.inactive_balance += value;
BalanceType::Inactive
} else {
staker.retired_balance += value;
BalanceType::Retired
};
self.balance += value;

// Create the receipt.
let receipt = AddStakeReceipt {
credited_balance: credited_balance.clone(),
};

if credited_balance == BalanceType::Active {
// If we are delegating to a validator, we need to update it.
if let Some(validator_address) = &staker.delegation {
// Check that the delegation is still valid, i.e. the validator hasn't been deleted.
store.expect_validator(validator_address)?;
self.increase_stake_to_validator(store, validator_address, value);
}
}

// Build the return logs
tx_logger.push_log(Log::Stake {
staker_address: staker_address.clone(),
validator_address: staker.delegation.clone(),
value,
credited_balance,
});

// Update the staker entry.
store.put_staker(staker_address, staker);

Ok(())
Ok(receipt)
}

/// Reverts a stake transaction.
Expand All @@ -326,24 +352,38 @@ impl StakingContract {
store: &mut StakingContractStoreWrite,
staker_address: &Address,
value: Coin,
receipt: AddStakeReceipt,
tx_logger: &mut TransactionLog,
) -> Result<(), AccountError> {
// Get the staker.
let mut staker = store.expect_staker(staker_address)?;

// If we are delegating to a validator, we need to update it too.
if let Some(validator_address) = &staker.delegation {
self.decrease_stake_from_validator(store, validator_address, value);
if receipt.credited_balance == BalanceType::Active {
if let Some(validator_address) = &staker.delegation {
self.decrease_stake_from_validator(store, validator_address, value);
}
}

// Update the staker's and staking contract's balances.
staker.active_balance -= value;
match receipt.credited_balance {
BalanceType::Active => {
staker.active_balance -= value;
}
BalanceType::Inactive => {
staker.inactive_balance -= value;
}
BalanceType::Retired => {
staker.retired_balance -= value;
}
}
self.balance -= value;

tx_logger.push_log(Log::Stake {
staker_address: staker_address.clone(),
validator_address: staker.delegation.clone(),
value,
credited_balance: receipt.credited_balance,
});

// Update the staker entry.
Expand Down
12 changes: 10 additions & 2 deletions primitives/account/src/account/staking_contract/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl AccountTransactionInteraction for StakingContract {
}
IncomingStakingTransactionData::AddStake { staker_address } => self
.add_stake(&mut store, &staker_address, transaction.value, tx_logger)
.map(|_| None),
.map(|receipt| Some(receipt.into())),
IncomingStakingTransactionData::UpdateStaker {
new_delegation,
reactivate_all_stake,
Expand Down Expand Up @@ -281,7 +281,15 @@ impl AccountTransactionInteraction for StakingContract {
self.revert_create_staker(&mut store, &staker_address, transaction.value, tx_logger)
}
IncomingStakingTransactionData::AddStake { staker_address } => {
self.revert_add_stake(&mut store, &staker_address, transaction.value, tx_logger)
let receipt = receipt.ok_or(AccountError::InvalidReceipt)?.try_into()?;

self.revert_add_stake(
&mut store,
&staker_address,
transaction.value,
receipt,
tx_logger,
)
}
IncomingStakingTransactionData::UpdateStaker { proof, .. } => {
// Get the staker address from the proof.
Expand Down
3 changes: 3 additions & 0 deletions primitives/account/src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use nimiq_transaction::{
Transaction,
};

use crate::BalanceType;

#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
// Renaming affects only the struct names and thus their tag, the "type" field.
#[serde(rename_all = "kebab-case", tag = "type")]
Expand Down Expand Up @@ -105,6 +107,7 @@ pub enum Log {
staker_address: Address,
validator_address: Option<Address>,
value: Coin,
credited_balance: BalanceType,
},

#[serde(rename_all = "camelCase")]
Expand Down
Loading