radix-engine 1.3.1

Reference implementation of Radix Engine, from the Radix DLT project.
Documentation
use crate::blueprints::resource::{
    fungible_vault, non_fungible_vault, BurnFungibleResourceEvent, BurnNonFungibleResourceEvent,
    MintFungibleResourceEvent, MintNonFungibleResourceEvent,
};
use crate::system::system_db_reader::SystemDatabaseReader;
use crate::transaction::{BalanceChange, StateUpdateSummary};
use radix_common::prelude::scrypto_decode;
use radix_common::traits::ScryptoEvent;
use radix_common::types::ResourceAddress;
use radix_engine_interface::api::ObjectModuleId;
use radix_engine_interface::types::{Emitter, EventTypeIdentifier};
use radix_rust::prelude::IndexMap;
use radix_rust::{btreeset, indexmap};
use radix_substate_store_interface::interface::SubstateDatabase;
use sbor::rust::ops::AddAssign;
use sbor::rust::ops::Neg;
use sbor::rust::vec::Vec;

pub fn reconcile_resource_state_and_events<'a, S: SubstateDatabase>(
    summary: &StateUpdateSummary,
    events: &Vec<(EventTypeIdentifier, Vec<u8>)>,
    system_db: SystemDatabaseReader<'a, S>,
) {
    let mut resource_changes_from_state = compute_resource_changes_from_state(summary);
    resource_changes_from_state.retain(|_, change| !change.prune_and_check_if_zero());

    let mut resource_changes_from_resman_events =
        compute_resource_changes_from_resman_events(events);
    resource_changes_from_resman_events.retain(|_, change| !change.prune_and_check_if_zero());

    let mut resource_changes_from_vault_events =
        compute_resource_changes_from_vault_events(events, &system_db);
    resource_changes_from_vault_events.retain(|_, change| !change.prune_and_check_if_zero());

    if resource_changes_from_state.ne(&resource_changes_from_vault_events)
        || resource_changes_from_vault_events.ne(&resource_changes_from_resman_events)
    {
        panic!("Txn Resource Reconciliation failed:\nState Changes: {:#?}\nResource Event Changes: {:#?}\nVault Event Changes: {:#?}",
               resource_changes_from_state,
               resource_changes_from_resman_events,
               resource_changes_from_vault_events,
        );
    }
}

fn compute_resource_changes_from_state(
    summary: &StateUpdateSummary,
) -> IndexMap<ResourceAddress, BalanceChange> {
    let mut resource_changes: IndexMap<ResourceAddress, BalanceChange> = indexmap!();
    for (_vault_id, (resource, change)) in &summary.vault_balance_changes {
        resource_changes
            .entry(*resource)
            .and_modify(|cur| {
                cur.add_assign(change.clone());
            })
            .or_insert(change.clone());
    }
    resource_changes
}

fn compute_resource_changes_from_resman_events(
    events: &Vec<(EventTypeIdentifier, Vec<u8>)>,
) -> IndexMap<ResourceAddress, BalanceChange> {
    let mut resource_changes_from_resman_events: IndexMap<ResourceAddress, BalanceChange> =
        indexmap!();

    for (event_id, event) in events {
        let (address, change) = match event_id.0 {
            Emitter::Method(node_id, ObjectModuleId::Main)
                if node_id.is_global_fungible_resource_manager() =>
            {
                let address = ResourceAddress::new_or_panic(node_id.0);

                let change = match event_id.1.as_str() {
                    MintFungibleResourceEvent::EVENT_NAME => {
                        let mint: MintFungibleResourceEvent = scrypto_decode(event).unwrap();
                        BalanceChange::Fungible(mint.amount)
                    }
                    BurnFungibleResourceEvent::EVENT_NAME => {
                        let burn: BurnFungibleResourceEvent = scrypto_decode(event).unwrap();
                        BalanceChange::Fungible(burn.amount.neg())
                    }
                    _ => continue,
                };

                (address, change)
            }

            Emitter::Method(node_id, ObjectModuleId::Main)
                if node_id.is_global_non_fungible_resource_manager() =>
            {
                let address = ResourceAddress::new_or_panic(node_id.0);

                let change = match event_id.1.as_str() {
                    MintNonFungibleResourceEvent::EVENT_NAME => {
                        let mint: MintNonFungibleResourceEvent = scrypto_decode(event).unwrap();
                        BalanceChange::NonFungible {
                            added: mint.ids.into_iter().collect(),
                            removed: btreeset!(),
                        }
                    }
                    BurnNonFungibleResourceEvent::EVENT_NAME => {
                        let burn: BurnNonFungibleResourceEvent = scrypto_decode(event).unwrap();
                        BalanceChange::NonFungible {
                            added: btreeset!(),
                            removed: burn.ids.into_iter().collect(),
                        }
                    }
                    _ => continue,
                };

                (address, change)
            }
            _ => continue,
        };

        resource_changes_from_resman_events
            .entry(address)
            .and_modify(|cur| {
                cur.add_assign(change.clone());
            })
            .or_insert(change.clone());
    }

    resource_changes_from_resman_events
}

fn compute_resource_changes_from_vault_events<'a, S: SubstateDatabase>(
    events: &Vec<(EventTypeIdentifier, Vec<u8>)>,
    system_db: &SystemDatabaseReader<'a, S>,
) -> IndexMap<ResourceAddress, BalanceChange> {
    let mut resource_changes_from_vault_events: IndexMap<ResourceAddress, BalanceChange> =
        indexmap!();

    for (event_id, event) in events {
        let (address, change) = match event_id.0 {
            Emitter::Method(node_id, ObjectModuleId::Main)
                if node_id.is_internal_fungible_vault() =>
            {
                let address: ResourceAddress = system_db
                    .get_object_info(node_id)
                    .unwrap()
                    .get_outer_object()
                    .try_into()
                    .unwrap();

                let change = match event_id.1.as_str() {
                    fungible_vault::DepositEvent::EVENT_NAME => {
                        let deposit: fungible_vault::DepositEvent = scrypto_decode(event).unwrap();
                        BalanceChange::Fungible(deposit.amount)
                    }
                    fungible_vault::WithdrawEvent::EVENT_NAME => {
                        let withdraw: fungible_vault::WithdrawEvent =
                            scrypto_decode(event).unwrap();
                        BalanceChange::Fungible(withdraw.amount.neg())
                    }
                    fungible_vault::RecallEvent::EVENT_NAME => {
                        let recall: fungible_vault::RecallEvent = scrypto_decode(event).unwrap();
                        BalanceChange::Fungible(recall.amount.neg())
                    }
                    fungible_vault::PayFeeEvent::EVENT_NAME => {
                        let recall: fungible_vault::PayFeeEvent = scrypto_decode(event).unwrap();
                        BalanceChange::Fungible(recall.amount.neg())
                    }
                    _ => continue,
                };

                (address, change)
            }

            Emitter::Method(node_id, ObjectModuleId::Main)
                if node_id.is_internal_non_fungible_vault() =>
            {
                let address: ResourceAddress = system_db
                    .get_object_info(node_id)
                    .unwrap()
                    .get_outer_object()
                    .try_into()
                    .unwrap();

                let change = match event_id.1.as_str() {
                    non_fungible_vault::DepositEvent::EVENT_NAME => {
                        let deposit: non_fungible_vault::DepositEvent =
                            scrypto_decode(event).unwrap();
                        BalanceChange::NonFungible {
                            added: deposit.ids.into_iter().collect(),
                            removed: btreeset!(),
                        }
                    }
                    non_fungible_vault::WithdrawEvent::EVENT_NAME => {
                        let withdraw: non_fungible_vault::WithdrawEvent =
                            scrypto_decode(event).unwrap();
                        BalanceChange::NonFungible {
                            added: btreeset!(),
                            removed: withdraw.ids.into_iter().collect(),
                        }
                    }
                    non_fungible_vault::RecallEvent::EVENT_NAME => {
                        let recall: non_fungible_vault::RecallEvent =
                            scrypto_decode(event).unwrap();
                        BalanceChange::NonFungible {
                            added: btreeset!(),
                            removed: recall.ids.into_iter().collect(),
                        }
                    }
                    _ => continue,
                };

                (address, change)
            }

            _ => continue,
        };

        resource_changes_from_vault_events
            .entry(address)
            .and_modify(|cur| {
                cur.add_assign(change.clone());
            })
            .or_insert(change.clone());
    }

    resource_changes_from_vault_events
}