Skip to content

Commit

Permalink
Accounts balances check
Browse files Browse the repository at this point in the history
  • Loading branch information
azarovh committed Jan 31, 2024
1 parent 585a50b commit 870f2fd
Show file tree
Hide file tree
Showing 22 changed files with 403 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,86 +13,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
collections::{btree_map::Entry, BTreeMap},
num::NonZeroU64,
};
use std::{collections::BTreeMap, num::NonZeroU64};

use common::{
chain::{
output_value::OutputValue, timelock::OutputTimeLock, AccountCommand, AccountSpending,
AccountType, ChainConfig, ConstraintsAccumulatorVersion, DelegationId, PoolId, TxInput,
TxOutput, UtxoOutPoint,
AccountsBalancesCheckVersion, ChainConfig, DelegationId, PoolId, TxInput, TxOutput,
UtxoOutPoint,
},
primitives::{Amount, BlockHeight, CoinOrTokenId, Fee, Subsidy},
};
use utils::ensure;

use super::{accumulated_fee::AccumulatedFee, insert_or_increase, Error};

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
enum GenericAccountId {
Delegation(DelegationId),
}

impl From<AccountSpending> for GenericAccountId {
fn from(account: AccountSpending) -> Self {
match account {
AccountSpending::DelegationBalance(id, _) => Self::Delegation(id),
}
}
}

impl From<GenericAccountId> for AccountType {
fn from(value: GenericAccountId) -> Self {
match value {
GenericAccountId::Delegation(id) => AccountType::Delegation(id),
}
}
}

struct AccountTracker<'a, DelegationBalanceGetterFn> {
balances: BTreeMap<GenericAccountId, Amount>,

delegation_balance_getter: &'a DelegationBalanceGetterFn,
}

impl<'a, DelegationBalanceGetterFn> AccountTracker<'a, DelegationBalanceGetterFn>
where
DelegationBalanceGetterFn: Fn(DelegationId) -> Result<Option<Amount>, Error>,
{
fn new(delegation_balance_getter: &'a DelegationBalanceGetterFn) -> Self {
Self {
balances: BTreeMap::new(),
delegation_balance_getter,
}
}

fn spend_from_account(
&mut self,
account: GenericAccountId,
amount: Amount,
) -> Result<(), Error> {
match self.balances.entry(account) {
Entry::Vacant(e) => {
let balance = match account {
GenericAccountId::Delegation(id) => (self.delegation_balance_getter)(id)?,
}
.ok_or(Error::AccountBalanceNotFound(account.into()))?;
let new_balance =
(balance - amount).ok_or(Error::NegativeAccountBalance(account.into()))?;
e.insert(new_balance);
}
Entry::Occupied(mut e) => {
let balance = e.get_mut();
*balance =
(*balance - amount).ok_or(Error::NegativeAccountBalance(account.into()))?;
}
};
Ok(())
}
}

/// `ConstrainedValueAccumulator` helps avoiding messy inputs/outputs combinations analysis by
/// providing a set of properties that should be satisfied. For example instead of checking that
/// all outputs are timelocked when the pool is decommissioned `ConstrainedValueAccumulator` gives a way
Expand Down Expand Up @@ -139,7 +73,6 @@ impl ConstrainedValueAccumulator {

let mut accumulator = Self::new();
let mut total_fee_deducted = Amount::ZERO;
let mut account_tracker = AccountTracker::new(&delegation_balance_getter);

for (input, input_utxo) in inputs.iter().zip(inputs_utxos.iter()) {
match input {
Expand All @@ -159,7 +92,6 @@ impl ConstrainedValueAccumulator {
chain_config,
block_height,
outpoint.account(),
&mut account_tracker,
&delegation_balance_getter,
)?;
}
Expand Down Expand Up @@ -263,7 +195,6 @@ impl ConstrainedValueAccumulator {
chain_config: &ChainConfig,
block_height: BlockHeight,
account: &AccountSpending,
account_tracker: &mut AccountTracker<DelegationBalanceGetterFn>,
delegation_balance_getter: &DelegationBalanceGetterFn,
) -> Result<(), Error>
where
Expand All @@ -275,20 +206,17 @@ impl ConstrainedValueAccumulator {
.chainstate_upgrades()
.version_at_height(block_height)
.1
.constraints_accumulator_version()
.accounts_balances_version()
{
ConstraintsAccumulatorVersion::V0 => {
AccountsBalancesCheckVersion::V0 => {
let delegation_balance = delegation_balance_getter(*delegation_id)?
.ok_or(Error::DelegationBalanceNotFound(*delegation_id))?;
ensure!(
*spend_amount <= delegation_balance,
Error::AttemptToPrintMoney(CoinOrTokenId::Coin)
);
}
ConstraintsAccumulatorVersion::V1 => {
account_tracker
.spend_from_account(account.clone().into(), *spend_amount)?;
}
AccountsBalancesCheckVersion::V1 => {}
}

let maturity_distance =
Expand Down
6 changes: 1 addition & 5 deletions chainstate/constraints-value-accumulator/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// limitations under the License.

use common::{
chain::{AccountType, DelegationId, PoolId, UtxoOutPoint},
chain::{DelegationId, PoolId, UtxoOutPoint},
primitives::CoinOrTokenId,
};

Expand Down Expand Up @@ -44,8 +44,4 @@ pub enum Error {
SpendingNonSpendableOutput(UtxoOutPoint),
#[error("Balance not found for delegation `{0}`")]
DelegationBalanceNotFound(DelegationId),
#[error("Account balance not found for `{0:?}`")]
AccountBalanceNotFound(AccountType),
#[error("Negative account balance for `{0:?}`")]
NegativeAccountBalance(AccountType),
}
6 changes: 4 additions & 2 deletions chainstate/src/detail/ban_score.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ impl BanScore for EpochSealError {
impl BanScore for IOPolicyError {
fn ban_score(&self) -> u32 {
match self {
IOPolicyError::PoSAccountingError(err) => err.ban_score(),
IOPolicyError::InvalidInputTypeInReward => 100,
IOPolicyError::InvalidOutputTypeInReward => 100,
IOPolicyError::InvalidInputTypeInTx => 100,
Expand All @@ -485,6 +486,9 @@ impl BanScore for IOPolicyError {
IOPolicyError::ProduceBlockInTx => 100,
IOPolicyError::MultipleAccountCommands => 100,
IOPolicyError::AttemptToUseAccountInputInReward => 100,
IOPolicyError::AccountBalanceNotFound(_) => 0,
IOPolicyError::NegativeAccountBalance(_) => 100,
IOPolicyError::AccountBalanceOverflow(_) => 100,
}
}
}
Expand All @@ -503,8 +507,6 @@ impl BanScore for constraints_value_accumulator::Error {
constraints_value_accumulator::Error::SpendingNonSpendableOutput(_) => 100,
constraints_value_accumulator::Error::AttemptToViolateFeeRequirements => 100,
constraints_value_accumulator::Error::DelegationBalanceNotFound(_) => 0,
constraints_value_accumulator::Error::AccountBalanceNotFound(_) => 0,
constraints_value_accumulator::Error::NegativeAccountBalance(_) => 100,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion chainstate/test-framework/src/framework_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct TestFrameworkBuilder {
custom_orphan_error_hook: Option<Arc<OrphanErrorHandler>>,
time_getter: Option<TimeGetter>,
time_value: Option<Arc<SeqCstAtomicU64>>,
tx_verification_strategy: TxVerificationStrategy, // FIXME: make it option: if none then chose strategy randomly
tx_verification_strategy: TxVerificationStrategy,
initial_time_since_genesis: u64,
}

Expand Down
8 changes: 4 additions & 4 deletions chainstate/test-suite/src/tests/chainstate_storage_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use common::{
chain::{
output_value::OutputValue,
tokens::{make_token_id, NftIssuance, TokenAuxiliaryData, TokenIssuanceV0},
ChainstateUpgrade, ConstraintsAccumulatorVersion, Destination, NetUpgrades,
AccountsBalancesCheckVersion, ChainstateUpgrade, Destination, NetUpgrades,
NftIdMismatchCheck, OutPointSourceId, RewardDistributionVersion, TokenIssuanceVersion,
TokensFeeVersion, TokensTickerMaxLengthVersion, Transaction, TxInput, TxOutput,
UtxoOutPoint,
Expand Down Expand Up @@ -122,7 +122,7 @@ fn store_fungible_token_v0(#[case] seed: Seed) {
TokensFeeVersion::V1,
TokensTickerMaxLengthVersion::V1,
NftIdMismatchCheck::Yes,
ConstraintsAccumulatorVersion::V1,
AccountsBalancesCheckVersion::V1,
),
)])
.unwrap(),
Expand Down Expand Up @@ -203,7 +203,7 @@ fn store_nft_v0(#[case] seed: Seed) {
TokensFeeVersion::V1,
TokensTickerMaxLengthVersion::V1,
NftIdMismatchCheck::Yes,
ConstraintsAccumulatorVersion::V1,
AccountsBalancesCheckVersion::V1,
),
)])
.unwrap(),
Expand Down Expand Up @@ -389,7 +389,7 @@ fn store_aux_data_from_issue_nft(#[case] seed: Seed) {
TokensFeeVersion::V1,
TokensTickerMaxLengthVersion::V1,
NftIdMismatchCheck::Yes,
ConstraintsAccumulatorVersion::V1,
AccountsBalancesCheckVersion::V1,
),
)])
.unwrap(),
Expand Down
Loading

0 comments on commit 870f2fd

Please sign in to comment.