From 61599637badf57169be2ffc2e0b41e0affcc995d Mon Sep 17 00:00:00 2001 From: Omar Date: Fri, 13 Dec 2024 12:45:59 +0300 Subject: [PATCH 1/5] Add a general subintent transaction type --- .../src/transaction_v1/manifest.rs | 2 + .../src/transaction_types/interface.rs | 46 +- .../traverser/types/general_subintent.rs | 183 +++ .../transaction_types/traverser/types/mod.rs | 2 + .../src/transaction_types/types.rs | 5 + .../tests/transaction_types.rs | 1302 ++++++++--------- 6 files changed, 841 insertions(+), 699 deletions(-) create mode 100644 crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs diff --git a/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs b/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs index 43aca4c1..eadd446c 100644 --- a/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs +++ b/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs @@ -392,6 +392,7 @@ impl From for ReservedInstruction { #[derive(Clone, Debug, Enum)] pub enum ManifestClass { + GeneralSubintent, General, Transfer, PoolContribution, @@ -405,6 +406,7 @@ pub enum ManifestClass { impl From for ManifestClass { fn from(value: CoreManifestClass) -> Self { match value { + CoreManifestClass::GeneralSubintent => Self::GeneralSubintent, CoreManifestClass::General => Self::General, CoreManifestClass::Transfer => Self::Transfer, CoreManifestClass::PoolContribution => Self::PoolContribution, diff --git a/crates/radix-engine-toolkit/src/transaction_types/interface.rs b/crates/radix-engine-toolkit/src/transaction_types/interface.rs index 7be8f9c2..9a60a82d 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/interface.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/interface.rs @@ -15,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +// TODO: Refactor the functions in here into a single function perhaps through +// some form of parsing modes, but we need to deduplicate the logic. + //! Functions that expose the transaction types functionality without exposing //! any of the implementation details of how the module finds and determines //! the transaction types. @@ -44,6 +47,8 @@ pub fn statically_analyze( StaticAccountResourceMovementsDetector::default(); let mut general_transaction_detector = GeneralDetector::default(); + let mut general_subintent_transaction_detector = + GeneralSubintentDetector::default(); let mut transfer_transaction_detector = TransferDetector::default(); let mut pool_contribution_detector = PoolContributionDetector::default(); let mut pool_redemption_detector = PoolRedemptionDetector::default(); @@ -62,6 +67,7 @@ pub fn statically_analyze( &mut reserved_instructions_detector, &mut account_resource_movements_detector, &mut general_transaction_detector, + &mut general_subintent_transaction_detector, &mut transfer_transaction_detector, &mut pool_contribution_detector, &mut pool_redemption_detector, @@ -82,6 +88,10 @@ pub fn statically_analyze( let (account_withdraws, account_deposits) = account_resource_movements_detector.output(); let classification = [ + ( + ManifestClass::GeneralSubintent, + general_subintent_transaction_detector.is_valid(), + ), ( ManifestClass::General, general_transaction_detector.is_valid(), @@ -146,6 +156,8 @@ pub fn statically_analyze_and_validate( StaticAccountResourceMovementsDetector::default(); let mut general_transaction_detector = GeneralDetector::default(); + let mut general_subintent_transaction_detector = + GeneralSubintentDetector::default(); let mut transfer_transaction_detector = TransferDetector::default(); let mut pool_contribution_detector = PoolContributionDetector::default(); let mut pool_redemption_detector = PoolRedemptionDetector::default(); @@ -164,6 +176,7 @@ pub fn statically_analyze_and_validate( &mut reserved_instructions_detector, &mut account_resource_movements_detector, &mut general_transaction_detector, + &mut general_subintent_transaction_detector, &mut transfer_transaction_detector, &mut pool_contribution_detector, &mut pool_redemption_detector, @@ -184,6 +197,10 @@ pub fn statically_analyze_and_validate( let (account_withdraws, account_deposits) = account_resource_movements_detector.output(); let classification = [ + ( + ManifestClass::GeneralSubintent, + general_subintent_transaction_detector.is_valid(), + ), ( ManifestClass::General, general_transaction_detector.is_valid(), @@ -262,6 +279,8 @@ pub fn classify_manifest( StaticAccountResourceMovementsDetector::default(); let mut general_transaction_detector = GeneralDetector::default(); + let mut general_subintent_transaction_detector = + GeneralSubintentDetector::default(); let mut transfer_transaction_detector = TransferDetector::default(); let mut pool_contribution_detector = PoolContributionDetector::default(); let mut pool_redemption_detector = PoolRedemptionDetector::default(); @@ -280,6 +299,7 @@ pub fn classify_manifest( &mut reserved_instructions_detector, &mut account_resource_movements_detector, &mut general_transaction_detector, + &mut general_subintent_transaction_detector, &mut transfer_transaction_detector, &mut pool_contribution_detector, &mut pool_redemption_detector, @@ -293,6 +313,10 @@ pub fn classify_manifest( // Extracting the data out of the detectors and into the ManifestSummary [ + ( + ManifestClass::GeneralSubintent, + general_subintent_transaction_detector.is_valid(), + ), ( ManifestClass::General, general_transaction_detector.is_valid(), @@ -352,6 +376,8 @@ pub fn dynamically_analyze( let newly_created_non_fungibles = receipt.new_non_fungibles(); let mut general_transaction_detector = GeneralDetector::default(); + let mut general_subintent_transaction_detector = + GeneralSubintentDetector::default(); let mut transfer_transaction_detector = TransferDetector::default(); let mut pool_contribution_detector = PoolContributionDetector::default(); let mut pool_redemption_detector = PoolRedemptionDetector::default(); @@ -370,6 +396,7 @@ pub fn dynamically_analyze( &mut reserved_instructions_detector, &mut account_resource_movements_detector, &mut general_transaction_detector, + &mut general_subintent_transaction_detector, &mut transfer_transaction_detector, &mut pool_contribution_detector, &mut pool_redemption_detector, @@ -486,17 +513,14 @@ pub fn dynamically_analyze( k, v.into_iter() .map(|(badge, operation)| { - ( - badge, - match operation { - Update::Set(()) => { - Operation::Added - } - Update::Remove => { - Operation::Removed - } - }, - ) + (badge, match operation { + Update::Set(()) => { + Operation::Added + } + Update::Remove => { + Operation::Removed + } + }) }) .collect(), ) diff --git a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs new file mode 100644 index 00000000..c2f05b5b --- /dev/null +++ b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs @@ -0,0 +1,183 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use radix_transactions::manifest::*; +use radix_transactions::prelude::*; +use scrypto::prelude::*; + +use radix_engine_interface::blueprints::account::*; + +use crate::transaction_types::*; + +pub struct GeneralSubintentDetector { + is_valid: bool, + is_yield_to_parent_present: bool, +} + +impl GeneralSubintentDetector { + pub fn is_valid(&self) -> bool { + self.is_valid && self.is_yield_to_parent_present + } + + pub fn output(self) -> Option<()> { + if self.is_valid() { Some(()) } else { None } + } +} + +impl StaticAnalysisCallback for GeneralSubintentDetector { + fn on_finish(&mut self, instructions_count: usize) { + if instructions_count == 0 { + self.is_valid = false + } + } + + fn on_instruction(&mut self, instruction: &InstructionV2, _: usize) { + // Control whether or not this is allowed or not based on: + // 1. Whether the instruction is allowed. + // 2. Whether the instruction contents are allowed. + self.is_valid &= match instruction { + /* Maybe Permitted - Need more info */ + InstructionV2::CallMethod(CallMethod { + address, + method_name, + .. + }) => { + Self::construct_fn_rules(address).is_fn_permitted(method_name) + } + /* Permitted */ + InstructionV2::TakeFromWorktop(..) + | InstructionV2::TakeNonFungiblesFromWorktop(..) + | InstructionV2::TakeAllFromWorktop(..) + | InstructionV2::ReturnToWorktop(..) + | InstructionV2::AssertWorktopContainsAny(..) + | InstructionV2::AssertWorktopContains(..) + | InstructionV2::AssertWorktopContainsNonFungibles(..) + | InstructionV2::AssertWorktopResourcesOnly(..) + | InstructionV2::AssertWorktopResourcesInclude(..) + | InstructionV2::AssertNextCallReturnsOnly(..) + | InstructionV2::AssertNextCallReturnsInclude(..) + | InstructionV2::AssertBucketContents(..) + | InstructionV2::PopFromAuthZone(..) + | InstructionV2::PushToAuthZone(..) + | InstructionV2::CreateProofFromAuthZoneOfAmount(..) + | InstructionV2::CreateProofFromAuthZoneOfNonFungibles(..) + | InstructionV2::CreateProofFromAuthZoneOfAll(..) + | InstructionV2::DropAuthZoneProofs(..) + | InstructionV2::DropAuthZoneRegularProofs(..) + | InstructionV2::DropAuthZoneSignatureProofs(..) + | InstructionV2::CreateProofFromBucketOfAmount(..) + | InstructionV2::CreateProofFromBucketOfNonFungibles(..) + | InstructionV2::CreateProofFromBucketOfAll(..) + | InstructionV2::CloneProof(..) + | InstructionV2::DropProof(..) + | InstructionV2::DropNamedProofs(..) + | InstructionV2::DropAllProofs(..) + | InstructionV2::CallFunction(..) + | InstructionV2::YieldToParent(_) + | InstructionV2::YieldToChild(_) + | InstructionV2::VerifyParent(_) => true, + /* Not Permitted */ + InstructionV2::BurnResource(..) + | InstructionV2::CallRoyaltyMethod(..) + | InstructionV2::CallMetadataMethod(..) + | InstructionV2::CallRoleAssignmentMethod(..) + | InstructionV2::CallDirectVaultMethod(..) + | InstructionV2::AllocateGlobalAddress(..) => false, + }; + + if let InstructionV2::YieldToParent(..) = instruction { + self.is_yield_to_parent_present = true; + } + } +} + +impl DynamicAnalysisCallback for GeneralSubintentDetector {} + +impl GeneralSubintentDetector { + fn construct_fn_rules(address: &DynamicGlobalAddress) -> FnRules { + match address { + DynamicGlobalAddress::Named(..) => FnRules::all_disallowed(), + DynamicGlobalAddress::Static(address) => { + address + .as_node_id() + .entity_type() + .map(|entity_type| { + match entity_type { + EntityType::GlobalAccount + | EntityType::GlobalPreallocatedSecp256k1Account + | EntityType::GlobalPreallocatedEd25519Account => { + FnRules { + allowed: &[ + /* All withdraw methods */ + ACCOUNT_WITHDRAW_IDENT, + ACCOUNT_WITHDRAW_NON_FUNGIBLES_IDENT, + /* All deposit methods */ + ACCOUNT_DEPOSIT_IDENT, + ACCOUNT_DEPOSIT_BATCH_IDENT, + ACCOUNT_TRY_DEPOSIT_OR_ABORT_IDENT, + ACCOUNT_TRY_DEPOSIT_BATCH_OR_ABORT_IDENT, + /* All proof creation methods */ + ACCOUNT_CREATE_PROOF_OF_AMOUNT_IDENT, + ACCOUNT_CREATE_PROOF_OF_NON_FUNGIBLES_IDENT, + /* Locking of fees */ + ACCOUNT_LOCK_FEE_IDENT, + ACCOUNT_LOCK_CONTINGENT_FEE_IDENT, + ACCOUNT_LOCK_FEE_AND_WITHDRAW_IDENT, + ACCOUNT_LOCK_FEE_AND_WITHDRAW_NON_FUNGIBLES_IDENT, + ], + disallowed: &[], + default: FnRule::Disallowed, + } + } + EntityType::GlobalGenericComponent + | EntityType::GlobalIdentity + | EntityType::GlobalPreallocatedSecp256k1Identity + | EntityType::GlobalPreallocatedEd25519Identity + | EntityType::InternalGenericComponent + | EntityType::GlobalAccountLocker => FnRules::all_allowed(), + /* Disallowed */ + EntityType::GlobalPackage + | EntityType::GlobalValidator + | EntityType::GlobalFungibleResourceManager + | EntityType::GlobalNonFungibleResourceManager + | EntityType::GlobalConsensusManager + | EntityType::InternalFungibleVault + | EntityType::InternalNonFungibleVault + | EntityType::InternalKeyValueStore + | EntityType::GlobalTransactionTracker + | EntityType::GlobalAccessController + | EntityType::GlobalOneResourcePool + | EntityType::GlobalTwoResourcePool + | EntityType::GlobalMultiResourcePool => { + FnRules::all_disallowed() + } + } + }) + .unwrap_or(FnRules::all_disallowed()) + } + } + } +} + +impl Default for GeneralSubintentDetector { + fn default() -> Self { + Self { + is_valid: true, + is_yield_to_parent_present: false, + } + } +} diff --git a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/mod.rs b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/mod.rs index c7365dca..d240368e 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/mod.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/mod.rs @@ -18,6 +18,7 @@ mod account_resource_movements; mod account_settings; mod general; +mod general_subintent; mod pool_contribution; mod pool_redemption; mod transfer; @@ -28,6 +29,7 @@ mod validator_unstake; pub use account_resource_movements::*; pub use account_settings::*; pub use general::*; +pub use general_subintent::*; pub use pool_contribution::*; pub use pool_redemption::*; pub use transfer::*; diff --git a/crates/radix-engine-toolkit/src/transaction_types/types.rs b/crates/radix-engine-toolkit/src/transaction_types/types.rs index 7ba5e500..84f4eeba 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/types.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/types.rs @@ -129,6 +129,11 @@ pub struct DynamicAnalysis { /// are the classes that the Radix Engine Toolkit supports. #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum ManifestClass { + /// A subintent manifest which satisfies the general rules allowed for in + /// general transactions and that includes a [`YieldToParent`] instruction. + /// + /// [`YieldToParent`]: radix_transactions::manifest::YieldToParent + GeneralSubintent, /// A general manifest that involves any amount of arbitrary components /// and packages where nothing more concrete can be said about the manifest /// and its nature. diff --git a/crates/radix-engine-toolkit/tests/transaction_types.rs b/crates/radix-engine-toolkit/tests/transaction_types.rs index 00c03139..738e7a37 100644 --- a/crates/radix-engine-toolkit/tests/transaction_types.rs +++ b/crates/radix-engine-toolkit/tests/transaction_types.rs @@ -20,9 +20,8 @@ use radix_engine::system::system_modules::execution_trace::ResourceSpecifier; use radix_engine_interface::blueprints::account::*; use radix_engine_interface::blueprints::consensus_manager::*; use radix_engine_interface::blueprints::pool::*; +use radix_engine_toolkit::functions::transaction_v2::subintent_manifest::statically_analyze_and_validate; use radix_engine_toolkit::transaction_types::*; -use radix_engine_toolkit::utils::network_definition_from_network_id; -use radix_transactions::manifest::{compile, MockBlobProvider}; use radix_transactions::prelude::*; use scrypto_test::prelude::*; @@ -122,27 +121,21 @@ fn lock_fee_still_keeps_the_transfer_classification_but_adds_a_reserved_instruct assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account1]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account2]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::Transfer, ManifestClass::General] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account2 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::Transfer, + ManifestClass::General + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account2 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( &dynamic_analysis.detailed_classification[0], @@ -214,27 +207,21 @@ fn simple_transfer_satisfies_the_transfer_and_general_transaction_types() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account1]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account2]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::Transfer, ManifestClass::General] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account2 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::Transfer, + ManifestClass::General + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account2 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( dynamic_analysis.detailed_classification[0], @@ -309,29 +296,23 @@ fn non_simple_transfer_satisfies_the_transfer_and_general_transaction_types() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account1]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account2]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::Transfer, ManifestClass::General] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account2 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), - ] - } - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::Transfer, + ManifestClass::General + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account2 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( dynamic_analysis.detailed_classification[0], @@ -405,22 +386,16 @@ fn transfers_with_try_deposit_or_refund_are_invalid() { assert_eq!(static_analysis.accounts_deposited_into, indexset![account2]); assert_eq!(static_analysis.classification, indexset![]); - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account2 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account2 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert_eq!(dynamic_analysis.newly_created_non_fungibles.len(), 0); @@ -485,22 +460,16 @@ fn lock_fee_is_recognized_as_a_reserved_instruction1() { assert_eq!(static_analysis.accounts_deposited_into, indexset![account2]); assert_eq!(static_analysis.classification, indexset![]); - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account2 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account2 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert_eq!(dynamic_analysis.newly_created_non_fungibles.len(), 0); @@ -564,22 +533,16 @@ fn lock_fee_is_recognized_as_a_reserved_instruction2() { assert_eq!(static_analysis.accounts_deposited_into, indexset![account2]); assert_eq!(static_analysis.classification, indexset![]); - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account2 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) - ] - } - ); + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account2 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10))) + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert_eq!(dynamic_analysis.newly_created_non_fungibles.len(), 0); @@ -640,20 +603,16 @@ fn faucet_fee_xrd_is_recognized_as_a_general_transaction() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account1]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::General] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::General + ]); assert!(dynamic_analysis.account_withdraws.is_empty()); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10_000))), - ] - } - ); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10_000))), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( dynamic_analysis.detailed_classification[0], @@ -718,20 +677,16 @@ fn account_deposit_is_recognized_as_a_method_that_requires_auth() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account1]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::General] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::General + ]); assert!(dynamic_analysis.account_withdraws.is_empty()); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10_000))), - ] - } - ); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Guaranteed(dec!(10_000))), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( dynamic_analysis.detailed_classification[0], @@ -791,20 +746,16 @@ fn account_deposit_batch_is_recognized_as_a_method_that_requires_auth() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account1]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::General] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::General + ]); assert!(dynamic_analysis.account_withdraws.is_empty()); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Predicted(Predicted { value: dec!(10_000), instruction_index: 1 })), - ] - } - ); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Predicted(Predicted { value: dec!(10_000), instruction_index: 1 })), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( dynamic_analysis.detailed_classification[0], @@ -869,20 +820,16 @@ fn instruction_index_of_predicted_bucket_is_its_creation_instruction() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account1]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::General] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::General + ]); assert!(dynamic_analysis.account_withdraws.is_empty()); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account1 => vec![ - ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Predicted(Predicted { value: dec!(10_000), instruction_index: 1 })), - ] - } - ); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account1 => vec![ + ResourceIndicator::Fungible(XRD, FungibleResourceIndicator::Predicted(Predicted { value: dec!(10_000), instruction_index: 1 })), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert!(matches!( dynamic_analysis.detailed_classification[0], @@ -995,86 +942,79 @@ fn pool_contribution_transactions_are_recognized() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::PoolContribution] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account => vec![ - /* One pool contribution */ - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - /* Two pool contribution */ - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource2, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - /* Multi pool contribution */ - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource2, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource3, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource4, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account => vec![ - /* One Pool Units */ - ResourceIndicator::Fungible( - one_pool_unit, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(100), - instruction_index: 3 - } - ) - ), - /* Two Pool Units */ - ResourceIndicator::Fungible( - two_pool_unit, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(100), - instruction_index: 9 - } - ) - ), - /* Multi Pool Units */ - ResourceIndicator::Fungible( - multi_pool_unit, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(100), - instruction_index: 15 - } - ) - ), - ] - } - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::PoolContribution + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account => vec![ + /* One pool contribution */ + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + /* Two pool contribution */ + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource2, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + /* Multi pool contribution */ + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource2, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource3, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource4, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account => vec![ + /* One Pool Units */ + ResourceIndicator::Fungible( + one_pool_unit, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(100), + instruction_index: 3 + } + ) + ), + /* Two Pool Units */ + ResourceIndicator::Fungible( + two_pool_unit, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(100), + instruction_index: 9 + } + ) + ), + /* Multi Pool Units */ + ResourceIndicator::Fungible( + multi_pool_unit, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(100), + instruction_index: 15 + } + ) + ), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); let [ @@ -1086,43 +1026,39 @@ fn pool_contribution_transactions_are_recognized() { else { panic!("Unexpected contents") }; - assert_eq!( - pool_addresses.clone(), - indexset![one_pool, two_pool, multi_pool,] - ); - assert_eq!( - pool_contributions.clone(), - vec![ - TrackedPoolContribution { - pool_address: one_pool, - contributed_resources: indexmap! { - resource1 => dec!(100) - }, - pool_units_resource_address: one_pool_unit, - pool_units_amount: dec!(100) + assert_eq!(pool_addresses.clone(), indexset![ + one_pool, two_pool, multi_pool, + ]); + assert_eq!(pool_contributions.clone(), vec![ + TrackedPoolContribution { + pool_address: one_pool, + contributed_resources: indexmap! { + resource1 => dec!(100) }, - TrackedPoolContribution { - pool_address: two_pool, - contributed_resources: indexmap! { - resource1 => dec!(100), - resource2 => dec!(100) - }, - pool_units_resource_address: two_pool_unit, - pool_units_amount: dec!(100) + pool_units_resource_address: one_pool_unit, + pool_units_amount: dec!(100) + }, + TrackedPoolContribution { + pool_address: two_pool, + contributed_resources: indexmap! { + resource1 => dec!(100), + resource2 => dec!(100) }, - TrackedPoolContribution { - pool_address: multi_pool, - contributed_resources: indexmap! { - resource1 => dec!(100), - resource2 => dec!(100), - resource3 => dec!(100), - resource4 => dec!(100) - }, - pool_units_resource_address: multi_pool_unit, - pool_units_amount: dec!(100) + pool_units_resource_address: two_pool_unit, + pool_units_amount: dec!(100) + }, + TrackedPoolContribution { + pool_address: multi_pool, + contributed_resources: indexmap! { + resource1 => dec!(100), + resource2 => dec!(100), + resource3 => dec!(100), + resource4 => dec!(100) }, - ] - ); + pool_units_resource_address: multi_pool_unit, + pool_units_amount: dec!(100) + }, + ]); assert_eq!(dynamic_analysis.newly_created_non_fungibles.len(), 0); } @@ -1206,102 +1142,95 @@ fn multi_resource_pool_contribution_with_change_is_correctly_handled() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::PoolContribution] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource2, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource3, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource4, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource2, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource3, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - resource4, - FungibleResourceIndicator::Guaranteed(dec!(50)) - ), - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - multi_pool_unit, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(100), - instruction_index: 5 - } - ) - ), - ResourceIndicator::Fungible( - multi_pool_unit, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(50), - instruction_index: 11 - } - ) - ), - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(50), - instruction_index: 11 - } - ) - ), - ResourceIndicator::Fungible( - resource2, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(50), - instruction_index: 11 - } - ) - ), - ResourceIndicator::Fungible( - resource3, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(50), - instruction_index: 11 - } - ) - ), - ] - } - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::PoolContribution + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource2, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource3, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource4, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource2, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource3, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + resource4, + FungibleResourceIndicator::Guaranteed(dec!(50)) + ), + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + multi_pool_unit, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(100), + instruction_index: 5 + } + ) + ), + ResourceIndicator::Fungible( + multi_pool_unit, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(50), + instruction_index: 11 + } + ) + ), + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(50), + instruction_index: 11 + } + ) + ), + ResourceIndicator::Fungible( + resource2, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(50), + instruction_index: 11 + } + ) + ), + ResourceIndicator::Fungible( + resource3, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(50), + instruction_index: 11 + } + ) + ), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); let [ @@ -1314,33 +1243,30 @@ fn multi_resource_pool_contribution_with_change_is_correctly_handled() { panic!("Unexpected contents") }; assert_eq!(pool_addresses.clone(), indexset![multi_pool,]); - assert_eq!( - pool_contributions.clone(), - vec![ - TrackedPoolContribution { - pool_address: multi_pool, - contributed_resources: indexmap! { - resource1 => dec!(100), - resource2 => dec!(100), - resource3 => dec!(100), - resource4 => dec!(100) - }, - pool_units_resource_address: multi_pool_unit, - pool_units_amount: dec!(100) + assert_eq!(pool_contributions.clone(), vec![ + TrackedPoolContribution { + pool_address: multi_pool, + contributed_resources: indexmap! { + resource1 => dec!(100), + resource2 => dec!(100), + resource3 => dec!(100), + resource4 => dec!(100) }, - TrackedPoolContribution { - pool_address: multi_pool, - contributed_resources: indexmap! { - resource1 => dec!(50), - resource2 => dec!(50), - resource3 => dec!(50), - resource4 => dec!(50) - }, - pool_units_resource_address: multi_pool_unit, - pool_units_amount: dec!(50) - } - ] - ); + pool_units_resource_address: multi_pool_unit, + pool_units_amount: dec!(100) + }, + TrackedPoolContribution { + pool_address: multi_pool, + contributed_resources: indexmap! { + resource1 => dec!(50), + resource2 => dec!(50), + resource3 => dec!(50), + resource4 => dec!(50) + }, + pool_units_resource_address: multi_pool_unit, + pool_units_amount: dec!(50) + } + ]); assert_eq!(dynamic_analysis.newly_created_non_fungibles.len(), 0); } @@ -1405,10 +1331,9 @@ fn pool_redemption_transactions_are_recognized() { .try_deposit_entire_worktop_or_abort(account, None) .build(); ledger - .execute_manifest( - manifest, - vec![NonFungibleGlobalId::from_public_key(&pk)], - ) + .execute_manifest(manifest, vec![NonFungibleGlobalId::from_public_key( + &pk, + )]) .expect_commit_success(); let manifest = ManifestBuilder::new() @@ -1489,74 +1414,67 @@ fn pool_redemption_transactions_are_recognized() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::PoolRedemption] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - one_pool_unit, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - two_pool_unit, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - multi_pool_unit, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account => vec![ - /* One pool contribution */ - ResourceIndicator::Fungible( - resource1, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(300), - instruction_index: 9 - } - ) - ), - ResourceIndicator::Fungible( - resource2, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(200), - instruction_index: 9 - } - ) - ), - ResourceIndicator::Fungible( - resource3, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(100), - instruction_index: 9 - } - ) - ), - ResourceIndicator::Fungible( - resource4, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(100), - instruction_index: 9 - } - ) - ), - ] - } - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::PoolRedemption + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + one_pool_unit, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + two_pool_unit, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + multi_pool_unit, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account => vec![ + /* One pool contribution */ + ResourceIndicator::Fungible( + resource1, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(300), + instruction_index: 9 + } + ) + ), + ResourceIndicator::Fungible( + resource2, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(200), + instruction_index: 9 + } + ) + ), + ResourceIndicator::Fungible( + resource3, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(100), + instruction_index: 9 + } + ) + ), + ResourceIndicator::Fungible( + resource4, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(100), + instruction_index: 9 + } + ) + ), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); let [ @@ -1568,43 +1486,39 @@ fn pool_redemption_transactions_are_recognized() { else { panic!("Unexpected contents") }; - assert_eq!( - pool_addresses.clone(), - indexset![one_pool, two_pool, multi_pool,] - ); - assert_eq!( - pool_redemptions.clone(), - vec![ - TrackedPoolRedemption { - pool_address: one_pool, - redeemed_resources: indexmap! { - resource1 => dec!(100) - }, - pool_units_resource_address: one_pool_unit, - pool_units_amount: dec!(100) + assert_eq!(pool_addresses.clone(), indexset![ + one_pool, two_pool, multi_pool, + ]); + assert_eq!(pool_redemptions.clone(), vec![ + TrackedPoolRedemption { + pool_address: one_pool, + redeemed_resources: indexmap! { + resource1 => dec!(100) }, - TrackedPoolRedemption { - pool_address: two_pool, - redeemed_resources: indexmap! { - resource1 => dec!(100), - resource2 => dec!(100) - }, - pool_units_resource_address: two_pool_unit, - pool_units_amount: dec!(100) + pool_units_resource_address: one_pool_unit, + pool_units_amount: dec!(100) + }, + TrackedPoolRedemption { + pool_address: two_pool, + redeemed_resources: indexmap! { + resource1 => dec!(100), + resource2 => dec!(100) }, - TrackedPoolRedemption { - pool_address: multi_pool, - redeemed_resources: indexmap! { - resource1 => dec!(100), - resource2 => dec!(100), - resource3 => dec!(100), - resource4 => dec!(100) - }, - pool_units_resource_address: multi_pool_unit, - pool_units_amount: dec!(100) + pool_units_resource_address: two_pool_unit, + pool_units_amount: dec!(100) + }, + TrackedPoolRedemption { + pool_address: multi_pool, + redeemed_resources: indexmap! { + resource1 => dec!(100), + resource2 => dec!(100), + resource3 => dec!(100), + resource4 => dec!(100) }, - ] - ); + pool_units_resource_address: multi_pool_unit, + pool_units_amount: dec!(100) + }, + ]); assert_eq!(dynamic_analysis.newly_created_non_fungibles.len(), 0); } @@ -1670,41 +1584,34 @@ fn validator_stake_transactions_are_recognized() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::ValidatorStake] - ); - - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - XRD, - FungibleResourceIndicator::Guaranteed(dec!(200)) + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::ValidatorStake + ]); + + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + XRD, + FungibleResourceIndicator::Guaranteed(dec!(200)) + ) + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + stake_unit1, + FungibleResourceIndicator::Predicted( + Predicted { value: dec!(100), instruction_index: 5 } ) - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - stake_unit1, - FungibleResourceIndicator::Predicted( - Predicted { value: dec!(100), instruction_index: 5 } - ) - ), - ResourceIndicator::Fungible( - stake_unit2, - FungibleResourceIndicator::Predicted( - Predicted { value: dec!(100), instruction_index: 5 } - ) - ), - ] - } - ); + ), + ResourceIndicator::Fungible( + stake_unit2, + FungibleResourceIndicator::Predicted( + Predicted { value: dec!(100), instruction_index: 5 } + ) + ), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert_eq!( dynamic_analysis.detailed_classification[0], @@ -1810,10 +1717,9 @@ fn validator_unstake_transactions_are_recognized() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::ValidatorUnstake] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::ValidatorUnstake + ]); let nf_id_local_1 = NonFungibleLocalId::from_str( "{9da60161aa56f3dc-b05ee091e6e496eb-926b11ceb384a4cb-16af5319924a3426}", @@ -1824,58 +1730,52 @@ fn validator_unstake_transactions_are_recognized() { ) .unwrap(); - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - stake_unit1, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ResourceIndicator::Fungible( - stake_unit2, - FungibleResourceIndicator::Guaranteed(dec!(100)) - ), - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account => vec![ - ResourceIndicator::NonFungible( - claim_nft1, - NonFungibleResourceIndicator::ByAll { - predicted_amount: Predicted { - value: dec!(1), - instruction_index: 6 - }, - predicted_ids: Predicted { - value: indexset![ - nf_id_local_1.clone() - ], - instruction_index: 6 - }, - } - ), - ResourceIndicator::NonFungible( - claim_nft2, - NonFungibleResourceIndicator::ByAll { - predicted_amount: Predicted { - value: dec!(1), - instruction_index: 6 - }, - predicted_ids: Predicted { - value: indexset![ - nf_id_local_2.clone() - ], - instruction_index: 6 - }, - } - ), - ] - } - ); + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + stake_unit1, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ResourceIndicator::Fungible( + stake_unit2, + FungibleResourceIndicator::Guaranteed(dec!(100)) + ), + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account => vec![ + ResourceIndicator::NonFungible( + claim_nft1, + NonFungibleResourceIndicator::ByAll { + predicted_amount: Predicted { + value: dec!(1), + instruction_index: 6 + }, + predicted_ids: Predicted { + value: indexset![ + nf_id_local_1.clone() + ], + instruction_index: 6 + }, + } + ), + ResourceIndicator::NonFungible( + claim_nft2, + NonFungibleResourceIndicator::ByAll { + predicted_amount: Predicted { + value: dec!(1), + instruction_index: 6 + }, + predicted_ids: Predicted { + value: indexset![ + nf_id_local_2.clone() + ], + instruction_index: 6 + }, + } + ), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert_eq!( dynamic_analysis.detailed_classification[0], @@ -2040,10 +1940,9 @@ fn validator_claim_transactions_are_recognized() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![account]); assert_eq!(static_analysis.accounts_deposited_into, indexset![account]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::ValidatorClaim] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::ValidatorClaim + ]); let nf_id_local_1 = NonFungibleLocalId::from_str( "{88187e7fec84a59c-9713f20d4bdd245a-90c9c04347db595f-07a038d384ce12a4}", @@ -2054,53 +1953,47 @@ fn validator_claim_transactions_are_recognized() { ) .unwrap(); - assert_eq!( - dynamic_analysis.account_withdraws, - indexmap! { - account => vec![ - ResourceIndicator::NonFungible( - claim_nft1, - NonFungibleResourceIndicator::ByAmount { - amount: dec!(1), - predicted_ids: Predicted { - value: indexset![ - nf_id_local_1.clone() - ], - instruction_index: 0 - }, - } - ), - ResourceIndicator::NonFungible( - claim_nft2, - NonFungibleResourceIndicator::ByAmount { - amount: dec!(1), - predicted_ids: Predicted { - value: indexset![ - nf_id_local_2.clone() - ], - instruction_index: 1 - }, + assert_eq!(dynamic_analysis.account_withdraws, indexmap! { + account => vec![ + ResourceIndicator::NonFungible( + claim_nft1, + NonFungibleResourceIndicator::ByAmount { + amount: dec!(1), + predicted_ids: Predicted { + value: indexset![ + nf_id_local_1.clone() + ], + instruction_index: 0 + }, + } + ), + ResourceIndicator::NonFungible( + claim_nft2, + NonFungibleResourceIndicator::ByAmount { + amount: dec!(1), + predicted_ids: Predicted { + value: indexset![ + nf_id_local_2.clone() + ], + instruction_index: 1 + }, + } + ), + ] + }); + assert_eq!(dynamic_analysis.account_deposits, indexmap! { + account => vec![ + ResourceIndicator::Fungible( + XRD, + FungibleResourceIndicator::Predicted( + Predicted { + value: dec!(200), + instruction_index: 6 } - ), - ] - } - ); - assert_eq!( - dynamic_analysis.account_deposits, - indexmap! { - account => vec![ - ResourceIndicator::Fungible( - XRD, - FungibleResourceIndicator::Predicted( - Predicted { - value: dec!(200), - instruction_index: 6 - } - ) - ), - ] - } - ); + ) + ), + ] + }); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); assert_eq!( dynamic_analysis.detailed_classification[0], @@ -2235,10 +2128,9 @@ fn account_deposit_settings_changes_are_recognized() { assert_eq!(static_analysis.accounts_withdrawn_from, indexset![]); assert_eq!(static_analysis.accounts_deposited_into, indexset![]); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::AccountDepositSettingsUpdate] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::AccountDepositSettingsUpdate + ]); assert!(dynamic_analysis.account_withdraws.is_empty()); assert!(dynamic_analysis.account_deposits.is_empty()); assert_eq!(dynamic_analysis.new_entities, NewEntities::default()); @@ -2335,37 +2227,23 @@ fn presented_proofs_non_fungible() { //Act let manifest = ManifestBuilder::new() .lock_fee_from_faucet() - .create_proof_from_account_of_non_fungibles( - account_1, - address_1, - [NonFungibleLocalId::integer(1)], - ) - .create_proof_from_account_of_non_fungibles( - account_1, - address_2, - [NonFungibleLocalId::integer(3)], - ) - .create_proof_from_account_of_non_fungibles( - account_2, - address_3, - [ - NonFungibleLocalId::integer(2), - NonFungibleLocalId::integer(3), - ], - ) - .create_proof_from_account_of_non_fungibles( - account_1, - address_1, - [ - NonFungibleLocalId::integer(1), - NonFungibleLocalId::integer(2), - ], - ) - .create_proof_from_account_of_non_fungibles( - account_2, - address_3, - [NonFungibleLocalId::integer(2)], - ) + .create_proof_from_account_of_non_fungibles(account_1, address_1, [ + NonFungibleLocalId::integer(1), + ]) + .create_proof_from_account_of_non_fungibles(account_1, address_2, [ + NonFungibleLocalId::integer(3), + ]) + .create_proof_from_account_of_non_fungibles(account_2, address_3, [ + NonFungibleLocalId::integer(2), + NonFungibleLocalId::integer(3), + ]) + .create_proof_from_account_of_non_fungibles(account_1, address_1, [ + NonFungibleLocalId::integer(1), + NonFungibleLocalId::integer(2), + ]) + .create_proof_from_account_of_non_fungibles(account_2, address_3, [ + NonFungibleLocalId::integer(2), + ]) .build(); let (static_analysis, _) = ledger.summarize(manifest); @@ -2577,10 +2455,9 @@ fn account_locker_is_recognized_as_general_transaction() { dynamic_analysis.detailed_classification.len(), 1 ); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::General] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::General + ]); assert_eq_three!( static_analysis.presented_proofs.len(), dynamic_analysis.presented_proofs.len(), @@ -2645,8 +2522,57 @@ fn lock_fee_manifest_has_no_classification_except_general() { dynamic_analysis.detailed_classification.len(), 1 ); - assert_eq!( - static_analysis.classification, - indexset![ManifestClass::General] - ); + assert_eq!(static_analysis.classification, indexset![ + ManifestClass::General + ]); +} + +#[test] +fn subintent_manifest_of_transfer_is_a_general_subintent() { + let account = + ComponentAddress::new_or_panic([EntityType::GlobalAccount as u8; 30]); + subintent_manifest_classification_test( + ManifestBuilder::new_subintent_v2() + .lock_fee(account, dec!(10)) + .withdraw_from_account(account, XRD, 10) + .take_all_from_worktop(XRD, "bucket") + .try_deposit_or_abort(account, None, "bucket") + .yield_to_parent(()) + .build(), + true, + ) +} + +#[test] +fn subintent_manifest_of_transfer_with_metadata_update_is_not_a_general_subintent() + { + let account = + ComponentAddress::new_or_panic([EntityType::GlobalAccount as u8; 30]); + subintent_manifest_classification_test( + ManifestBuilder::new_subintent_v2() + .lock_fee(account, dec!(10)) + .withdraw_from_account(account, XRD, 10) + .take_all_from_worktop(XRD, "bucket") + .try_deposit_or_abort(account, None, "bucket") + .set_metadata(account, "some_key", "some_value") + .yield_to_parent(()) + .build(), + false, + ) +} + +fn subintent_manifest_classification_test( + subintent_manifest: SubintentManifestV2, + is_general_subintent: bool, +) { + let static_analysis = statically_analyze_and_validate(&subintent_manifest) + .expect("Static analysis failed"); + let contains_general_subintent_classification = static_analysis + .classification + .contains(&ManifestClass::GeneralSubintent); + if is_general_subintent { + assert!(contains_general_subintent_classification); + } else { + assert!(!contains_general_subintent_classification); + } } From c1d95a7c2aad70da8fcad56323b27c38e267975e Mon Sep 17 00:00:00 2001 From: Omar Date: Fri, 13 Dec 2024 13:29:35 +0300 Subject: [PATCH 2/5] Make changes to account owner keys a reserved instruction --- .../src/transaction_v1/manifest.rs | 14 +++ .../auxiliary/reserved_instructions.rs | 90 ++++++++++++------- .../src/transaction_types/types.rs | 2 + .../tests/transaction_types.rs | 87 ++++++++++++++++++ 4 files changed, 163 insertions(+), 30 deletions(-) diff --git a/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs b/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs index eadd446c..e1b5e8dd 100644 --- a/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs +++ b/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs @@ -360,13 +360,21 @@ impl ToNative for AccountDefaultDepositRule { pub enum ReservedInstruction { AccountLockFee, AccountSecurify, + AccountUpdateOwnerKeysMetadataField, IdentitySecurify, + IdentityUpdateOwnerKeysMetadataField, AccessControllerMethod, } impl From for CoreReservedInstruction { fn from(value: ReservedInstruction) -> Self { match value { + ReservedInstruction::AccountUpdateOwnerKeysMetadataField => { + Self::AccountUpdateOwnerKeysMetadataField + } + ReservedInstruction::IdentityUpdateOwnerKeysMetadataField => { + Self::IdentityUpdateOwnerKeysMetadataField + } ReservedInstruction::AccessControllerMethod => { Self::AccessControllerMethod } @@ -380,6 +388,12 @@ impl From for CoreReservedInstruction { impl From for ReservedInstruction { fn from(value: CoreReservedInstruction) -> Self { match value { + CoreReservedInstruction::AccountUpdateOwnerKeysMetadataField => { + Self::AccountUpdateOwnerKeysMetadataField + } + CoreReservedInstruction::IdentityUpdateOwnerKeysMetadataField => { + Self::IdentityUpdateOwnerKeysMetadataField + } CoreReservedInstruction::AccessControllerMethod => { Self::AccessControllerMethod } diff --git a/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs b/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs index 8494b75d..96df6205 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs @@ -39,36 +39,66 @@ impl ReservedInstructionsDetector { impl StaticAnalysisCallback for ReservedInstructionsDetector { fn on_instruction(&mut self, instruction: &InstructionV2, _: usize) { - let InstructionV2::CallMethod(CallMethod { - address, - method_name, - .. - }) = instruction - else { - return; - }; - - if is_account(address) - && contains!( - method_name => [ - ACCOUNT_LOCK_FEE_IDENT, - ACCOUNT_LOCK_FEE_AND_WITHDRAW_IDENT, - ACCOUNT_LOCK_FEE_AND_WITHDRAW_NON_FUNGIBLES_IDENT, - ] - ) - { - self.reserved_instructions - .insert(ReservedInstruction::AccountLockFee); - } else if is_account(address) && method_name == ACCOUNT_SECURIFY_IDENT { - self.reserved_instructions - .insert(ReservedInstruction::AccountSecurify); - } else if is_identity(address) && method_name == IDENTITY_SECURIFY_IDENT - { - self.reserved_instructions - .insert(ReservedInstruction::AccountLockFee); - } else if is_access_controller(address) { - self.reserved_instructions - .insert(ReservedInstruction::AccessControllerMethod); + // TODO: Make use of the typed manifest invocations - they would make + // some of the logic in here easier. + match instruction { + InstructionV2::CallMethod(CallMethod { + address, + method_name, + .. + }) => { + if is_account(address) + && contains!( + method_name => [ + ACCOUNT_LOCK_FEE_IDENT, + ACCOUNT_LOCK_FEE_AND_WITHDRAW_IDENT, + ACCOUNT_LOCK_FEE_AND_WITHDRAW_NON_FUNGIBLES_IDENT, + ] + ) + { + self.reserved_instructions + .insert(ReservedInstruction::AccountLockFee); + } else if is_account(address) + && method_name == ACCOUNT_SECURIFY_IDENT + { + self.reserved_instructions + .insert(ReservedInstruction::AccountSecurify); + } else if is_identity(address) + && method_name == IDENTITY_SECURIFY_IDENT + { + self.reserved_instructions + .insert(ReservedInstruction::AccountLockFee); + } else if is_access_controller(address) { + self.reserved_instructions + .insert(ReservedInstruction::AccessControllerMethod); + } + } + InstructionV2::CallMetadataMethod(CallMetadataMethod { + address, + method_name, + args, + }) => { + if method_name == METADATA_SET_IDENT { + // Attempt to decode the args as a metadata set call. If we + // fail then we have not technically detected a violation of + // the reserved instructions and we can just ignore this. + let Some(MetadataSetInput { key, .. }) = + manifest_encode(args) + .ok() + .and_then(|encoded| manifest_decode(&encoded).ok()) + else { + return; + }; + let is_owner_keys_metadata_key = key == "owner_keys"; + if is_account(address) && is_owner_keys_metadata_key { + self.reserved_instructions.insert(ReservedInstruction::AccountUpdateOwnerKeysMetadataField); + } else if is_identity(address) && is_owner_keys_metadata_key + { + self.reserved_instructions.insert(ReservedInstruction::IdentityUpdateOwnerKeysMetadataField); + } + } + } + _ => {} } } } diff --git a/crates/radix-engine-toolkit/src/transaction_types/types.rs b/crates/radix-engine-toolkit/src/transaction_types/types.rs index 84f4eeba..1bf12214 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/types.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/types.rs @@ -486,7 +486,9 @@ pub struct NewEntities { pub enum ReservedInstruction { AccountLockFee, AccountSecurify, + AccountUpdateOwnerKeysMetadataField, IdentitySecurify, + IdentityUpdateOwnerKeysMetadataField, AccessControllerMethod, } diff --git a/crates/radix-engine-toolkit/tests/transaction_types.rs b/crates/radix-engine-toolkit/tests/transaction_types.rs index 738e7a37..62c7f380 100644 --- a/crates/radix-engine-toolkit/tests/transaction_types.rs +++ b/crates/radix-engine-toolkit/tests/transaction_types.rs @@ -2576,3 +2576,90 @@ fn subintent_manifest_classification_test( assert!(!contains_general_subintent_classification); } } + +#[test] +fn update_to_account_owner_keys_is_reserved_instruction() { + let account = + ComponentAddress::new_or_panic([EntityType::GlobalAccount as u8; 30]); + reserved_instructions_test( + ManifestBuilder::new() + .set_metadata(account, "owner_keys", "not relevant") + .build(), + true, + false, + ) +} + +#[test] +fn update_to_identity_owner_keys_is_reserved_instruction() { + let identity = + ComponentAddress::new_or_panic([EntityType::GlobalIdentity as u8; 30]); + reserved_instructions_test( + ManifestBuilder::new() + .set_metadata(identity, "owner_keys", "not relevant") + .build(), + false, + true, + ) +} + +#[test] +fn update_to_component_owner_keys_is_not_reserved_instruction() { + let component = ComponentAddress::new_or_panic( + [EntityType::GlobalGenericComponent as u8; 30], + ); + reserved_instructions_test( + ManifestBuilder::new() + .set_metadata(component, "owner_keys", "not relevant") + .build(), + false, + false, + ) +} + +#[test] +fn update_to_account_non_owner_keys_field_is_not_reserved_instruction() { + let account = + ComponentAddress::new_or_panic([EntityType::GlobalAccount as u8; 30]); + reserved_instructions_test( + ManifestBuilder::new() + .set_metadata(account, "example", "not relevant") + .build(), + false, + false, + ) +} + +#[test] +fn update_to_identity_non_owner_keys_field_is_not_reserved_instruction() { + let identity = + ComponentAddress::new_or_panic([EntityType::GlobalIdentity as u8; 30]); + reserved_instructions_test( + ManifestBuilder::new() + .set_metadata(identity, "example", "not relevant") + .build(), + false, + false, + ) +} + +fn reserved_instructions_test( + manifest: TransactionManifestV1, + expect_account_owner_key_update: bool, + expect_identity_owner_key_update: bool, +) { + let StaticAnalysis { + reserved_instructions, + .. + } = statically_analyze(&manifest); + let [is_account_owner_key_update, is_identity_owner_key_update] = [ + ReservedInstruction::AccountUpdateOwnerKeysMetadataField, + ReservedInstruction::IdentityUpdateOwnerKeysMetadataField, + ] + .map(|expected| reserved_instructions.contains(&expected)); + assert_eq!(is_account_owner_key_update, expect_account_owner_key_update); + assert_eq!( + is_identity_owner_key_update, + expect_identity_owner_key_update + ); +} From 88d4a3a420d60596dad4876c07deef2646c71845 Mon Sep 17 00:00:00 2001 From: Omar Date: Fri, 13 Dec 2024 13:30:32 +0300 Subject: [PATCH 3/5] formatting --- .../src/blueprints/metadata.rs | 11 ++++------- .../src/builder/manifest_builder/builder_v1.rs | 2 +- .../src/builder/manifest_builder/builder_v2.rs | 2 +- .../src/functions/manifest_sbor.rs | 2 +- .../src/functions/transaction_v1/manifest.rs | 2 +- .../src/models/canonical_address_types.rs | 13 +++++-------- .../tests/manifest_sbor.rs | 2 +- .../radix-engine-toolkit/tests/non_fungible.rs | 18 ++++++------------ .../tests/schema_visitor.rs | 2 +- .../radix-engine-toolkit/tests/scrypto_sbor.rs | 4 ++-- .../tests/test_runner_extension.rs | 17 ++++++----------- .../src/scrypto/programmatic/utils.rs | 2 +- .../tests/programmatic_scrypto_sbor.rs | 11 ++++------- 13 files changed, 34 insertions(+), 54 deletions(-) diff --git a/crates/radix-engine-toolkit-uniffi/src/blueprints/metadata.rs b/crates/radix-engine-toolkit-uniffi/src/blueprints/metadata.rs index 8a98588e..f77e0bba 100644 --- a/crates/radix-engine-toolkit-uniffi/src/blueprints/metadata.rs +++ b/crates/radix-engine-toolkit-uniffi/src/blueprints/metadata.rs @@ -44,13 +44,10 @@ impl ToNative for MetadataInit { None => None, }; - Ok(( - key, - NativeKeyValueStoreInitEntry:: { - lock: value.lock, - value: metadata, - }, - )) + Ok((key, NativeKeyValueStoreInitEntry:: { + lock: value.lock, + value: metadata, + })) }) .collect:: TransactionReceiptV1 { - self.preview_manifest( - manifest, - vec![], - 0, - PreviewFlags { - use_free_credit: true, - assume_all_signature_proofs: true, - skip_epoch_check: true, - disable_auth: true, - }, - ) + self.preview_manifest(manifest, vec![], 0, PreviewFlags { + use_free_credit: true, + assume_all_signature_proofs: true, + skip_epoch_check: true, + disable_auth: true, + }) } fn summarize( diff --git a/crates/sbor-json/src/scrypto/programmatic/utils.rs b/crates/sbor-json/src/scrypto/programmatic/utils.rs index e011a932..a1e5a07f 100644 --- a/crates/sbor-json/src/scrypto/programmatic/utils.rs +++ b/crates/sbor-json/src/scrypto/programmatic/utils.rs @@ -16,7 +16,7 @@ // under the License. use super::value::ProgrammaticScryptoValue; -use super::visitor::{traverse, AddressNetworkMismatchVisitor}; +use super::visitor::{AddressNetworkMismatchVisitor, traverse}; pub fn value_contains_network_mismatch( value: &ProgrammaticScryptoValue, diff --git a/crates/sbor-json/tests/programmatic_scrypto_sbor.rs b/crates/sbor-json/tests/programmatic_scrypto_sbor.rs index 1d47d5e4..0d87dd62 100644 --- a/crates/sbor-json/tests/programmatic_scrypto_sbor.rs +++ b/crates/sbor-json/tests/programmatic_scrypto_sbor.rs @@ -107,13 +107,10 @@ pub fn payload_serialized_with_schema_can_be_deserialized_as_no_schema_programma .unwrap(); // Assert - assert_eq!( - deserialized, - ProgrammaticScryptoValue::Enum { - discriminator: 2, - fields: vec![ProgrammaticScryptoValue::U8 { value: 1 }] - } - ) + assert_eq!(deserialized, ProgrammaticScryptoValue::Enum { + discriminator: 2, + fields: vec![ProgrammaticScryptoValue::U8 { value: 1 }] + }) } #[test] From cabe7d6e21cbed51d3103117c727a5ea262c85ec Mon Sep 17 00:00:00 2001 From: Omar Date: Fri, 13 Dec 2024 13:31:42 +0300 Subject: [PATCH 4/5] Disallow yielding to children in general subintent --- .../transaction_types/traverser/types/general_subintent.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs index c2f05b5b..62da5618 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs @@ -88,7 +88,6 @@ impl StaticAnalysisCallback for GeneralSubintentDetector { | InstructionV2::DropAllProofs(..) | InstructionV2::CallFunction(..) | InstructionV2::YieldToParent(_) - | InstructionV2::YieldToChild(_) | InstructionV2::VerifyParent(_) => true, /* Not Permitted */ InstructionV2::BurnResource(..) @@ -96,7 +95,8 @@ impl StaticAnalysisCallback for GeneralSubintentDetector { | InstructionV2::CallMetadataMethod(..) | InstructionV2::CallRoleAssignmentMethod(..) | InstructionV2::CallDirectVaultMethod(..) - | InstructionV2::AllocateGlobalAddress(..) => false, + | InstructionV2::AllocateGlobalAddress(..) + | InstructionV2::YieldToChild(_) => false, }; if let InstructionV2::YieldToParent(..) = instruction { From e44b01924609cd86aac7ecac9abef83a387462f4 Mon Sep 17 00:00:00 2001 From: Omar Date: Fri, 13 Dec 2024 18:33:02 +0300 Subject: [PATCH 5/5] resolve review comments --- .../src/transaction_v1/manifest.rs | 14 +++++++++ .../auxiliary/reserved_instructions.rs | 18 ++++++++++++ .../traverser/types/general_subintent.rs | 14 +++------ .../src/transaction_types/types.rs | 2 ++ .../tests/transaction_types.rs | 29 +++++++++++++++++++ 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs b/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs index e1b5e8dd..7365d875 100644 --- a/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs +++ b/crates/radix-engine-toolkit-uniffi/src/transaction_v1/manifest.rs @@ -360,8 +360,10 @@ impl ToNative for AccountDefaultDepositRule { pub enum ReservedInstruction { AccountLockFee, AccountSecurify, + AccountLockOwnerKeysMetadataField, AccountUpdateOwnerKeysMetadataField, IdentitySecurify, + IdentityLockOwnerKeysMetadataField, IdentityUpdateOwnerKeysMetadataField, AccessControllerMethod, } @@ -372,9 +374,15 @@ impl From for CoreReservedInstruction { ReservedInstruction::AccountUpdateOwnerKeysMetadataField => { Self::AccountUpdateOwnerKeysMetadataField } + ReservedInstruction::AccountLockOwnerKeysMetadataField => { + Self::AccountLockOwnerKeysMetadataField + } ReservedInstruction::IdentityUpdateOwnerKeysMetadataField => { Self::IdentityUpdateOwnerKeysMetadataField } + ReservedInstruction::IdentityLockOwnerKeysMetadataField => { + Self::IdentityLockOwnerKeysMetadataField + } ReservedInstruction::AccessControllerMethod => { Self::AccessControllerMethod } @@ -391,9 +399,15 @@ impl From for ReservedInstruction { CoreReservedInstruction::AccountUpdateOwnerKeysMetadataField => { Self::AccountUpdateOwnerKeysMetadataField } + CoreReservedInstruction::AccountLockOwnerKeysMetadataField => { + Self::AccountLockOwnerKeysMetadataField + } CoreReservedInstruction::IdentityUpdateOwnerKeysMetadataField => { Self::IdentityUpdateOwnerKeysMetadataField } + CoreReservedInstruction::IdentityLockOwnerKeysMetadataField => { + Self::IdentityLockOwnerKeysMetadataField + } CoreReservedInstruction::AccessControllerMethod => { Self::AccessControllerMethod } diff --git a/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs b/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs index 96df6205..3cd0446a 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/traverser/auxiliary/reserved_instructions.rs @@ -96,6 +96,24 @@ impl StaticAnalysisCallback for ReservedInstructionsDetector { { self.reserved_instructions.insert(ReservedInstruction::IdentityUpdateOwnerKeysMetadataField); } + } else if method_name == METADATA_LOCK_IDENT { + // Attempt to decode the args as a metadata set call. If we + // fail then we have not technically detected a violation of + // the reserved instructions and we can just ignore this. + let Some(MetadataLockInput { key, .. }) = + manifest_encode(args) + .ok() + .and_then(|encoded| manifest_decode(&encoded).ok()) + else { + return; + }; + let is_owner_keys_metadata_key = key == "owner_keys"; + if is_account(address) && is_owner_keys_metadata_key { + self.reserved_instructions.insert(ReservedInstruction::AccountLockOwnerKeysMetadataField); + } else if is_identity(address) && is_owner_keys_metadata_key + { + self.reserved_instructions.insert(ReservedInstruction::IdentityLockOwnerKeysMetadataField); + } } } _ => {} diff --git a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs index 62da5618..72206e6d 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/traverser/types/general_subintent.rs @@ -24,13 +24,13 @@ use radix_engine_interface::blueprints::account::*; use crate::transaction_types::*; pub struct GeneralSubintentDetector { - is_valid: bool, + is_all_instructions_allowed: bool, is_yield_to_parent_present: bool, } impl GeneralSubintentDetector { pub fn is_valid(&self) -> bool { - self.is_valid && self.is_yield_to_parent_present + self.is_all_instructions_allowed && self.is_yield_to_parent_present } pub fn output(self) -> Option<()> { @@ -39,17 +39,11 @@ impl GeneralSubintentDetector { } impl StaticAnalysisCallback for GeneralSubintentDetector { - fn on_finish(&mut self, instructions_count: usize) { - if instructions_count == 0 { - self.is_valid = false - } - } - fn on_instruction(&mut self, instruction: &InstructionV2, _: usize) { // Control whether or not this is allowed or not based on: // 1. Whether the instruction is allowed. // 2. Whether the instruction contents are allowed. - self.is_valid &= match instruction { + self.is_all_instructions_allowed &= match instruction { /* Maybe Permitted - Need more info */ InstructionV2::CallMethod(CallMethod { address, @@ -176,7 +170,7 @@ impl GeneralSubintentDetector { impl Default for GeneralSubintentDetector { fn default() -> Self { Self { - is_valid: true, + is_all_instructions_allowed: true, is_yield_to_parent_present: false, } } diff --git a/crates/radix-engine-toolkit/src/transaction_types/types.rs b/crates/radix-engine-toolkit/src/transaction_types/types.rs index 1bf12214..12bb8dfa 100644 --- a/crates/radix-engine-toolkit/src/transaction_types/types.rs +++ b/crates/radix-engine-toolkit/src/transaction_types/types.rs @@ -486,8 +486,10 @@ pub struct NewEntities { pub enum ReservedInstruction { AccountLockFee, AccountSecurify, + AccountLockOwnerKeysMetadataField, AccountUpdateOwnerKeysMetadataField, IdentitySecurify, + IdentityLockOwnerKeysMetadataField, IdentityUpdateOwnerKeysMetadataField, AccessControllerMethod, } diff --git a/crates/radix-engine-toolkit/tests/transaction_types.rs b/crates/radix-engine-toolkit/tests/transaction_types.rs index 62c7f380..c3b12196 100644 --- a/crates/radix-engine-toolkit/tests/transaction_types.rs +++ b/crates/radix-engine-toolkit/tests/transaction_types.rs @@ -2543,6 +2543,35 @@ fn subintent_manifest_of_transfer_is_a_general_subintent() { ) } +#[test] +fn subintent_manifest_of_transfer_and_verify_parent_is_a_general_subintent() { + let account = + ComponentAddress::new_or_panic([EntityType::GlobalAccount as u8; 30]); + subintent_manifest_classification_test( + ManifestBuilder::new_subintent_v2() + .lock_fee(account, dec!(10)) + .withdraw_from_account(account, XRD, 10) + .take_all_from_worktop(XRD, "bucket") + .try_deposit_or_abort(account, None, "bucket") + .verify_parent(rule!(allow_all)) + .yield_to_parent(()) + .build(), + true, + ) +} + +#[test] +fn subintent_manifest_with_yield_to_child_is_not_a_general_subintent() { + subintent_manifest_classification_test( + ManifestBuilder::new_subintent_v2() + .use_child("example", SubintentHash(Hash([0; 32]))) + .yield_to_child("example", ()) + .yield_to_parent(()) + .build(), + false, + ) +} + #[test] fn subintent_manifest_of_transfer_with_metadata_update_is_not_a_general_subintent() {