Skip to content

Commit

Permalink
Keep the final balances check
Browse files Browse the repository at this point in the history
  • Loading branch information
azarovh committed Feb 1, 2024
1 parent 870f2fd commit 616df83
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 13 deletions.
4 changes: 2 additions & 2 deletions chainstate/test-suite/src/tests/delegation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,8 +1350,8 @@ fn try_overspend_delegation(
AccountsBalancesCheckVersion::V0 => {
assert_eq!(
res.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::TransactionVerifierError(
tx_verifier::TransactionVerifierStorageError::PoSAccountingError(
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::PoSAccountingError(
pos_accounting::Error::AccountingError(
accounting::Error::ArithmeticErrorSumToUnsignedFailed
)
Expand Down
64 changes: 53 additions & 11 deletions chainstate/tx-verifier/src/transaction_verifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub use cached_operation::CachedOperation;

pub use input_output_policy::{calculate_tokens_burned_in_outputs, IOPolicyError};

use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};

use self::{
error::{ConnectTransactionError, TokensError},
Expand All @@ -69,8 +69,9 @@ use common::{
signature::Signable,
signed_transaction::SignedTransaction,
tokens::make_token_id,
AccountCommand, AccountNonce, AccountSpending, AccountType, Block, ChainConfig, GenBlock,
TokenIssuanceVersion, Transaction, TxInput, TxOutput, UtxoOutPoint,
AccountCommand, AccountNonce, AccountSpending, AccountType, AccountsBalancesCheckVersion,
Block, ChainConfig, DelegationId, GenBlock, TokenIssuanceVersion, Transaction, TxInput,
TxOutput, UtxoOutPoint,
},
primitives::{id::WithId, Amount, BlockHeight, Fee, Id, Idable},
};
Expand Down Expand Up @@ -308,9 +309,12 @@ where

fn connect_pos_accounting_outputs(
&mut self,
tx_source: TransactionSource,
tx_source: &TransactionSourceForConnect,
tx: &Transaction,
) -> Result<(), ConnectTransactionError> {
let mut check_delegation: Option<DelegationId> = None;
let mut delegations_with_spendings = BTreeSet::<DelegationId>::new();

// Process tx inputs in terms of pos accounting.
// Spending `CreateStakePool`, `ProduceBlockFromStake` utxos or an account input
// should result in either decommissioning a pool or spending share in accounting
Expand All @@ -319,11 +323,13 @@ where
.iter()
.filter_map(|input| match input {
TxInput::Utxo(ref outpoint) => {
self.spend_input_from_utxo(tx_source, outpoint).transpose()
self.spend_input_from_utxo(tx_source.into(), outpoint).transpose()
}
TxInput::Account(outpoint) => {
match outpoint.account() {
AccountSpending::DelegationBalance(delegation_id, withdraw_amount) => {
check_delegation = Some(*delegation_id);
delegations_with_spendings.insert(*delegation_id);
let res = self
.spend_input_from_account(
outpoint.nonce(),
Expand All @@ -333,7 +339,7 @@ where
// If the input spends from delegation account, this means the user is
// spending part of their share in the pool.
self.pos_accounting_adapter
.operations(tx_source)
.operations(tx_source.into())
.spend_share_from_delegation_id(
*delegation_id,
*withdraw_amount,
Expand All @@ -360,7 +366,7 @@ where
let res = if expected_pool_id == *pool_id {
if data.value() >= self.chain_config.as_ref().min_stake_pool_pledge() {
self.pos_accounting_adapter
.operations(tx_source)
.operations(tx_source.into())
.create_pool(*pool_id, data.as_ref().clone().into())
.map_err(ConnectTransactionError::PoSAccountingError)
} else {
Expand All @@ -387,7 +393,7 @@ where
Some(input_utxo_outpoint) => {
let res = self
.pos_accounting_adapter
.operations(tx_source)
.operations(tx_source.into())
.create_delegation_id(
*target_pool,
spend_destination.clone(),
Expand All @@ -405,7 +411,7 @@ where
TxOutput::DelegateStaking(amount, delegation_id) => {
let res = self
.pos_accounting_adapter
.operations(tx_source)
.operations(tx_source.into())
.delegate_staking(*delegation_id, *amount)
.map_err(ConnectTransactionError::PoSAccountingError);
Some(res)
Expand All @@ -420,11 +426,47 @@ where
})
.collect::<Result<Vec<_>, _>>()?;

match self
.chain_config
.as_ref()
.chainstate_upgrades()
.version_at_height(tx_source.expected_block_height())
.1
.accounts_balances_version()
{
AccountsBalancesCheckVersion::V0 => {
if let Some(delegation_id) = check_delegation {
let _ = self
.pos_accounting_adapter
.accounting_delta()
.get_delegation_balance(delegation_id)?
.ok_or(ConnectTransactionError::DelegationDataNotFound(
delegation_id,
))?;
}
}
AccountsBalancesCheckVersion::V1 => {
// Iterate over all delegations that have spending and get the balance.
// By retrieving the balance we apply all the deltas from the accounting and can verify
// that final balance is not negative.
// This check is not mandatory but a safe-net to ensure no overspends happen.
for delegation_id in delegations_with_spendings {
let _ = self
.pos_accounting_adapter
.accounting_delta()
.get_delegation_balance(delegation_id)?
.ok_or(ConnectTransactionError::DelegationDataNotFound(
delegation_id,
))?;
}
}
}

// Store pos accounting operations undos
if !inputs_undos.is_empty() || !outputs_undos.is_empty() {
let tx_undos = inputs_undos.into_iter().chain(outputs_undos).collect();
self.pos_accounting_block_undo
.get_or_create_block_undo(&tx_source)
.get_or_create_block_undo(&tx_source.into())
.insert_tx_undo(tx.get_id(), pos_accounting::AccountingTxUndo::new(tx_undos))
.map_err(ConnectTransactionError::AccountingBlockUndoError)
} else {
Expand Down Expand Up @@ -779,7 +821,7 @@ where
),
)?;

self.connect_pos_accounting_outputs(tx_source.into(), tx.transaction())?;
self.connect_pos_accounting_outputs(tx_source, tx.transaction())?;

self.connect_tokens_outputs(tx_source, tx.transaction())?;

Expand Down

0 comments on commit 616df83

Please sign in to comment.