radix-engine 1.3.1

Reference implementation of Radix Engine, from the Radix DLT project.
Documentation
use crate::blueprints::consensus_manager::{
    ConsensusManagerCurrentProposalStatisticFieldPayload,
    ConsensusManagerCurrentValidatorSetFieldPayload, ConsensusManagerField,
};
use crate::blueprints::resource::{
    FungibleResourceManagerField, FungibleResourceManagerTotalSupplyFieldPayload,
    FungibleVaultBalanceFieldPayload, FungibleVaultField, NonFungibleResourceManagerField,
    NonFungibleResourceManagerTotalSupplyFieldPayload, NonFungibleVaultBalanceFieldPayload,
    NonFungibleVaultCollection, NonFungibleVaultField,
};
use crate::internal_prelude::*;
use crate::system::checkers::ApplicationChecker;
use radix_common::math::Decimal;
use radix_common::prelude::{scrypto_decode, RESOURCE_PACKAGE};
use radix_common::types::{NodeId, ResourceAddress};
use radix_engine_interface::api::FieldIndex;
use radix_engine_interface::blueprints::consensus_manager::CONSENSUS_MANAGER_BLUEPRINT;
use radix_engine_interface::blueprints::resource::{
    FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, FUNGIBLE_VAULT_BLUEPRINT,
    NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, NON_FUNGIBLE_VAULT_BLUEPRINT,
};
use sbor::rust::collections::BTreeMap;
use sbor::rust::vec::Vec;

#[derive(Debug, Default)]
pub struct ResourceCounter {
    expected: Option<Decimal>,
    tracking_supply: Decimal,
}

#[derive(Debug, Default)]
pub struct ResourceDatabaseChecker {
    resources: BTreeMap<ResourceAddress, ResourceCounter>,
    non_fungible_vaults: BTreeMap<NodeId, ResourceCounter>,
    fungible_vaults: BTreeMap<NodeId, Decimal>,
}

#[derive(Debug, Default)]
pub struct ResourceDatabaseCheckerResults {
    pub num_resources: usize,
    pub total_supply: BTreeMap<ResourceAddress, Decimal>,
    pub vaults: BTreeMap<NodeId, Decimal>,
}

impl ApplicationChecker for ResourceDatabaseChecker {
    type ApplicationCheckerResults = ResourceDatabaseCheckerResults;

    fn on_field(
        &mut self,
        info: BlueprintInfo,
        node_id: NodeId,
        module_id: ModuleId,
        field_index: FieldIndex,
        value: &Vec<u8>,
    ) {
        if !info.blueprint_id.package_address.eq(&RESOURCE_PACKAGE) || module_id != ModuleId::Main {
            return;
        }

        match info.blueprint_id.blueprint_name.as_str() {
            FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT => {
                let field: FungibleResourceManagerField = field_index.try_into().unwrap();
                if field == FungibleResourceManagerField::TotalSupply {
                    let total_supply: FungibleResourceManagerTotalSupplyFieldPayload =
                        scrypto_decode(value).unwrap();
                    let address = ResourceAddress::new_or_panic(node_id.0);
                    let tracker = self.resources.entry(address).or_default();
                    tracker.expected = Some(total_supply.fully_update_and_into_latest_version());
                }
            }
            FUNGIBLE_VAULT_BLUEPRINT => {
                let field: FungibleVaultField = field_index.try_into().unwrap();
                match field {
                    FungibleVaultField::Balance => {
                        let vault_balance: FungibleVaultBalanceFieldPayload =
                            scrypto_decode(value).unwrap();
                        let address = ResourceAddress::new_or_panic(
                            info.outer_obj_info.expect().into_node_id().0,
                        );
                        let amount = vault_balance
                            .fully_update_and_into_latest_version()
                            .amount();

                        if amount.is_negative() {
                            panic!("Found Fungible Vault negative balance");
                        }

                        let tracker = self.resources.entry(address).or_default();
                        tracker.tracking_supply =
                            tracker.tracking_supply.checked_add(amount).unwrap();

                        self.fungible_vaults.insert(node_id, amount);
                    }
                    FungibleVaultField::LockedBalance => todo!(),
                    FungibleVaultField::FreezeStatus => todo!(),
                }
            }
            NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT => {
                let field: NonFungibleResourceManagerField = field_index.try_into().unwrap();
                if let NonFungibleResourceManagerField::TotalSupply = field {
                    let total_supply: NonFungibleResourceManagerTotalSupplyFieldPayload =
                        scrypto_decode(value).unwrap();
                    let address = ResourceAddress::new_or_panic(node_id.0);
                    let tracker = self.resources.entry(address).or_default();
                    tracker.expected = Some(total_supply.fully_update_and_into_latest_version());
                }
            }
            NON_FUNGIBLE_VAULT_BLUEPRINT => {
                let field: NonFungibleVaultField = field_index.try_into().unwrap();
                if field == NonFungibleVaultField::Balance {
                    let vault_balance: NonFungibleVaultBalanceFieldPayload =
                        scrypto_decode(value).unwrap();
                    let address = ResourceAddress::new_or_panic(
                        info.outer_obj_info.expect().into_node_id().0,
                    );
                    let tracker = self.resources.entry(address).or_default();
                    let vault_balance = vault_balance.fully_update_and_into_latest_version();
                    tracker.tracking_supply = tracker
                        .tracking_supply
                        .checked_add(vault_balance.amount)
                        .unwrap();

                    let non_fungible_vault_tracker =
                        self.non_fungible_vaults.entry(node_id).or_default();

                    if vault_balance.amount.is_negative() {
                        panic!("Found Non Fungible Vault negative balance");
                    }

                    non_fungible_vault_tracker.expected = Some(vault_balance.amount);
                }
            }
            CONSENSUS_MANAGER_BLUEPRINT => {
                let field: ConsensusManagerField = field_index.try_into().unwrap();
                let mut validator_count = 0;
                let mut stats_count = 0;
                match field {
                    ConsensusManagerField::CurrentValidatorSet => {
                        let validator_set: ConsensusManagerCurrentValidatorSetFieldPayload =
                            scrypto_decode(value).unwrap();

                        let mut prev = Decimal::MAX;
                        for validator in validator_set
                            .fully_update_and_into_latest_version()
                            .validator_set
                            .validators_by_stake_desc
                        {
                            if validator.1.stake > prev {
                                panic!("Validator set are not in DESC order");
                            }
                            prev = validator.1.stake;
                            validator_count += 1;
                        }
                    }
                    ConsensusManagerField::CurrentProposalStatistic => {
                        let stats: ConsensusManagerCurrentProposalStatisticFieldPayload =
                            scrypto_decode(value).unwrap();
                        stats_count = stats
                            .fully_update_and_into_latest_version()
                            .validator_statistics
                            .len();
                    }
                    _ => {}
                }

                if validator_count != stats_count {
                    panic!("Validator count != proposal statistics count")
                }
            }
            _ => {}
        }
    }

    fn on_collection_entry(
        &mut self,
        info: BlueprintInfo,
        node_id: NodeId,
        module_id: ModuleId,
        collection_index: CollectionIndex,
        _key: &Vec<u8>,
        _value: &Vec<u8>,
    ) {
        if !info.blueprint_id.package_address.eq(&RESOURCE_PACKAGE) || module_id != ModuleId::Main {
            return;
        }

        if let NON_FUNGIBLE_VAULT_BLUEPRINT = info.blueprint_id.blueprint_name.as_str() {
            let collection: NonFungibleVaultCollection = collection_index.try_into().unwrap();
            match collection {
                NonFungibleVaultCollection::NonFungibleIndex => {
                    let non_fungible_vault_tracker =
                        self.non_fungible_vaults.entry(node_id).or_default();
                    non_fungible_vault_tracker.tracking_supply = non_fungible_vault_tracker
                        .tracking_supply
                        .checked_add(Decimal::one())
                        .unwrap();
                }
            }
        }
    }

    fn on_finish(&self) -> Self::ApplicationCheckerResults {
        for (address, counter) in &self.non_fungible_vaults {
            if let Some(expected) = counter.expected {
                if !expected.eq(&counter.tracking_supply) {
                    panic!(
                        "Vault amount mismatch: {:?} index: {:?} tracked_supply: {:?}",
                        address, expected, counter.tracking_supply,
                    );
                }
            } else {
                panic!("Found non fungible vault with no amount index");
            }
        }

        let mut vaults: BTreeMap<NodeId, Decimal> = self
            .non_fungible_vaults
            .iter()
            .map(|(vault_id, counter)| (*vault_id, counter.tracking_supply))
            .collect();
        vaults.extend(self.fungible_vaults.clone());

        let mut total_supply = BTreeMap::new();

        for (address, tracker) in &self.resources {
            if let Some(total_supply) = tracker.expected {
                if !total_supply.eq(&tracker.tracking_supply) {
                    panic!(
                        "Total Supply mismatch: {:?} total_supply: {:?} tracked_supply: {:?}",
                        address, total_supply, tracker.tracking_supply,
                    );
                }
            }

            total_supply.insert(*address, tracker.tracking_supply);
        }

        ResourceDatabaseCheckerResults {
            num_resources: self.resources.len(),
            total_supply,
            vaults,
        }
    }
}