diff --git a/examples/everything/Cargo.lock b/examples/everything/Cargo.lock index b7b7e8f69a2..0e53802dba7 100644 --- a/examples/everything/Cargo.lock +++ b/examples/everything/Cargo.lock @@ -1396,6 +1396,7 @@ name = "sbor-derive-common" version = "1.3.0-dev" dependencies = [ "const-sha1", + "indexmap 2.2.6", "itertools", "proc-macro2", "quote", diff --git a/examples/hello-world/Cargo.lock b/examples/hello-world/Cargo.lock index a13c3c0bf0c..def72580315 100644 --- a/examples/hello-world/Cargo.lock +++ b/examples/hello-world/Cargo.lock @@ -1396,6 +1396,7 @@ name = "sbor-derive-common" version = "1.3.0-dev" dependencies = [ "const-sha1", + "indexmap 2.2.6", "itertools", "proc-macro2", "quote", diff --git a/examples/no-std/Cargo.lock b/examples/no-std/Cargo.lock index 509ff2ec923..5a7d684f6d7 100644 --- a/examples/no-std/Cargo.lock +++ b/examples/no-std/Cargo.lock @@ -580,6 +580,7 @@ name = "sbor-derive-common" version = "1.3.0-dev" dependencies = [ "const-sha1", + "indexmap", "itertools", "proc-macro2", "quote", diff --git a/radix-clis/src/replay/ledger_transaction.rs b/radix-clis/src/replay/ledger_transaction.rs index a06f51d5bd5..825be1dfee5 100644 --- a/radix-clis/src/replay/ledger_transaction.rs +++ b/radix-clis/src/replay/ledger_transaction.rs @@ -1,3 +1,4 @@ +use std::rc::Rc; use radix_common::prelude::*; use radix_engine_interface::blueprints::consensus_manager::*; use radix_engine_interface::prelude::system_execution; @@ -97,11 +98,12 @@ impl TransactionFullChildPreparable for PreparedRoundUpdateTransactionV1 { } impl PreparedRoundUpdateTransactionV1 { - pub fn get_executable(&self) -> Executable<'_> { + pub fn get_executable(&self) -> Executable { Executable::new( - &self.encoded_instructions, + Rc::new(self.encoded_instructions.clone()), &self.references, - &self.blobs, + Rc::new(self.blobs.clone()), + vec![], ExecutionContext { intent_hash: TransactionIntentHash::NotToCheck { intent_hash: self.summary.hash, @@ -109,16 +111,15 @@ impl PreparedRoundUpdateTransactionV1 { epoch_range: None, payload_size: 0, num_of_signature_validations: 0, - auth_zone_params: AuthZoneParams { - initial_proofs: btreeset!(system_execution(SystemExecution::Validator)), - virtual_resources: BTreeSet::new(), - }, + auth_zone_params: AuthZoneParams::single_thread( + btreeset!(system_execution(SystemExecution::Validator)), + BTreeSet::new(), + ), costing_parameters: TransactionCostingParameters { tip_percentage: 0, free_credit_in_xrd: Decimal::ZERO, abort_when_loan_repaid: false, }, - pre_allocated_addresses: vec![], }, true, ) @@ -459,7 +460,7 @@ impl ValidatedLedgerTransaction { } } - pub fn get_executable(&self) -> Executable<'_> { + pub fn get_executable(&self) -> Executable { match &self.inner { ValidatedLedgerTransactionInner::Genesis(genesis) => match genesis.as_ref() { PreparedGenesisTransaction::Flash(_) => { diff --git a/radix-clis/src/replay/ledger_transaction_execution.rs b/radix-clis/src/replay/ledger_transaction_execution.rs index 5cba77aa48a..fe919ff5c6f 100644 --- a/radix-clis/src/replay/ledger_transaction_execution.rs +++ b/radix-clis/src/replay/ledger_transaction_execution.rs @@ -82,7 +82,7 @@ pub fn execute_prepared_ledger_transaction( &ExecutionConfig::for_genesis_transaction(network.clone()) .with_kernel_trace(trace) .with_cost_breakdown(trace), - &tx.get_executable(btreeset!(system_execution(SystemExecution::Protocol))), + Rc::new(tx.get_executable(btreeset!(system_execution(SystemExecution::Protocol)))), ); LedgerTransactionReceipt::Standard(receipt) } @@ -98,10 +98,10 @@ pub fn execute_prepared_ledger_transaction( &ExecutionConfig::for_notarized_transaction(network.clone()) .with_kernel_trace(trace) .with_cost_breakdown(trace), - &NotarizedTransactionValidator::new(ValidationConfig::default(network.id)) + Rc::new(NotarizedTransactionValidator::new(ValidationConfig::default(network.id)) .validate(tx.as_ref().clone()) .expect("Transaction validation failure") - .get_executable(), + .get_executable()), ); LedgerTransactionReceipt::Standard(receipt) } @@ -115,7 +115,7 @@ pub fn execute_prepared_ledger_transaction( &ExecutionConfig::for_system_transaction(network.clone()) .with_kernel_trace(trace) .with_cost_breakdown(trace), - &tx.get_executable(), + Rc::new(tx.get_executable()), ); LedgerTransactionReceipt::Standard(receipt) } diff --git a/radix-clis/src/resim/mod.rs b/radix-clis/src/resim/mod.rs index dfaf2ce4cf0..9ff7d6afdec 100644 --- a/radix-clis/src/resim/mod.rs +++ b/radix-clis/src/resim/mod.rs @@ -186,10 +186,10 @@ pub fn handle_system_transaction( vm_init, &ExecutionConfig::for_system_transaction(NetworkDefinition::simulator()) .with_kernel_trace(trace), - &transaction + Rc::new(transaction .prepare() .map_err(Error::TransactionPrepareError)? - .get_executable(initial_proofs), + .get_executable(initial_proofs)), ); if print_receipt { @@ -254,10 +254,10 @@ pub fn handle_manifest( &mut db, vm_init, &ExecutionConfig::for_test_transaction().with_kernel_trace(trace), - &transaction + Rc::new(transaction .prepare() .map_err(Error::TransactionPrepareError)? - .get_executable(initial_proofs), + .get_executable(initial_proofs)), ); if print_receipt { diff --git a/radix-engine-interface/src/api/mod.rs b/radix-engine-interface/src/api/mod.rs index 16882cb4a4f..e25f31d053b 100644 --- a/radix-engine-interface/src/api/mod.rs +++ b/radix-engine-interface/src/api/mod.rs @@ -10,6 +10,7 @@ pub mod key_value_entry_api; pub mod key_value_store_api; pub mod object_api; pub mod transaction_runtime_api; +pub mod thread_api; // Re-exports pub use actor_api::*; @@ -41,6 +42,7 @@ pub type FieldIndex = u8; pub type CollectionIndex = u8; use radix_common::prelude::*; +use crate::api::thread_api::SystemThreadApi; pub trait SystemApiError: fmt::Debug + ScryptoCategorize + ScryptoDecode {} @@ -58,6 +60,7 @@ pub trait SystemApi: + SystemFieldApi + SystemBlueprintApi + SystemCostingApi + + SystemThreadApi + SystemTransactionRuntimeApi + SystemExecutionTraceApi { diff --git a/radix-engine-interface/src/api/thread_api.rs b/radix-engine-interface/src/api/thread_api.rs new file mode 100644 index 00000000000..05bd2f554b7 --- /dev/null +++ b/radix-engine-interface/src/api/thread_api.rs @@ -0,0 +1,10 @@ +use crate::types::IndexedScryptoValue; + +pub trait SystemThreadApi { + + fn free_stack(&mut self, stack_id: usize) -> Result<(), E>; + + fn move_to_stack(&mut self, stack_id: usize, value: IndexedScryptoValue) -> Result<(), E>; + + fn switch_stack(&mut self, stack_id: usize) -> Result<(), E>; +} diff --git a/radix-engine-monkey-tests/tests/fuzz_kernel.rs b/radix-engine-monkey-tests/tests/fuzz_kernel.rs index a1e419ea1dd..af07e2c5416 100644 --- a/radix-engine-monkey-tests/tests/fuzz_kernel.rs +++ b/radix-engine-monkey-tests/tests/fuzz_kernel.rs @@ -2,19 +2,14 @@ use radix_common::prelude::*; use radix_engine::errors::{ BootloadingError, RejectionReason, RuntimeError, TransactionExecutionError, }; -use radix_engine::kernel::call_frame::{CallFrameMessage, StableReferenceType}; +use radix_engine::kernel::call_frame::{CallFrameMessage, RootCallFrameInitRefs, StableReferenceType}; use radix_engine::kernel::id_allocator::IdAllocator; use radix_engine::kernel::kernel::Kernel; use radix_engine::kernel::kernel_api::{ KernelApi, KernelInternalApi, KernelInvocation, KernelInvokeApi, KernelNodeApi, KernelSubstateApi, }; -use radix_engine::kernel::kernel_callback_api::{ - CallFrameReferences, CloseSubstateEvent, CreateNodeEvent, DrainSubstatesEvent, DropNodeEvent, - ExecutionReceipt, KernelCallbackObject, MoveModuleEvent, OpenSubstateEvent, ReadSubstateEvent, - RemoveSubstateEvent, ScanKeysEvent, ScanSortedSubstatesEvent, SetSubstateEvent, - WriteSubstateEvent, -}; +use radix_engine::kernel::kernel_callback_api::{CallFrameReferences, CloseSubstateEvent, CreateNodeEvent, DrainSubstatesEvent, DropNodeEvent, ExecutionReceipt, InvokeResult, KernelCallbackObject, MoveModuleEvent, OpenSubstateEvent, ReadSubstateEvent, RemoveSubstateEvent, ResumeResult, ScanKeysEvent, ScanSortedSubstatesEvent, SetSubstateEvent, WriteSubstateEvent}; use radix_engine::system::checkers::KernelDatabaseChecker; use radix_engine::track::{ to_state_updates, BootStore, CommitableSubstateStore, StoreCommitInfo, Track, @@ -24,7 +19,7 @@ use radix_engine_interface::prelude::*; use radix_substate_store_impls::memory_db::InMemorySubstateDatabase; use radix_substate_store_interface::db_key_mapper::SpreadPrefixKeyMapper; use radix_substate_store_interface::interface::{CommittableSubstateDatabase, SubstateDatabase}; -use radix_transactions::model::{Executable, PreAllocatedAddress}; +use radix_transactions::model::{Executable, ExecutableThread}; use rand::Rng; use rand_chacha::rand_core::SeedableRng; use rand_chacha::ChaCha8Rng; @@ -75,19 +70,13 @@ impl KernelCallbackObject for TestCallbackObject { fn init( _store: &mut S, - _executable: &Executable, + _executable: Rc, _init_input: Self::Init, - ) -> Result { - Ok(Self) + ) -> Result<(Self, Vec), RejectionReason> { + Ok((Self, vec![])) } - fn start>( - _: &mut Y, - _: &[u8], - _: &Vec, - _: &IndexSet, - _: &IndexMap>, - ) -> Result<(), RuntimeError> { + fn start>(_: &mut Y) -> Result<(), RuntimeError> { unreachable!() } @@ -98,20 +87,11 @@ impl KernelCallbackObject for TestCallbackObject { fn create_receipt( self, _track: Track, - _executable: &Executable, _result: Result<(), TransactionExecutionError>, ) -> Self::Receipt { TestReceipt } - fn verify_boot_ref_value( - &mut self, - _node_id: &NodeId, - _value: &IndexedScryptoValue, - ) -> Result { - Ok(StableReferenceType::Global) - } - fn on_pin_node(&mut self, _node_id: &NodeId) -> Result<(), RuntimeError> { Ok(()) } @@ -223,8 +203,12 @@ impl KernelCallbackObject for TestCallbackObject { fn invoke_upstream>( args: &IndexedScryptoValue, _api: &mut Y, - ) -> Result { - Ok(args.clone()) + ) -> Result { + Ok(InvokeResult::Done(args.clone())) + } + + fn resume_thread>(args: &IndexedScryptoValue, api: &mut Y) -> Result { + Ok(ResumeResult::Done(0, args.clone())) } fn auto_drop>( diff --git a/radix-engine-tests/benches/transfer.rs b/radix-engine-tests/benches/transfer.rs index 77591d9dd31..0c5f7f8c913 100644 --- a/radix-engine-tests/benches/transfer.rs +++ b/radix-engine-tests/benches/transfer.rs @@ -46,10 +46,10 @@ fn bench_transfer(c: &mut Criterion) { &mut substate_db, vm_init.clone(), &ExecutionConfig::for_notarized_transaction(NetworkDefinition::simulator()), - &TestTransaction::new_from_nonce(manifest, 1) + Rc::new(TestTransaction::new_from_nonce(manifest, 1) .prepare() .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)])), ) .expect_commit(true) .new_component_addresses()[0]; @@ -72,10 +72,10 @@ fn bench_transfer(c: &mut Criterion) { &mut substate_db, vm_init.clone(), &ExecutionConfig::for_notarized_transaction(NetworkDefinition::simulator()), - &TestTransaction::new_from_nonce(manifest.clone(), nonce) + Rc::new(TestTransaction::new_from_nonce(manifest.clone(), nonce) .prepare() .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)])), ) .expect_commit(true); } @@ -95,10 +95,10 @@ fn bench_transfer(c: &mut Criterion) { &mut substate_db, vm_init.clone(), &ExecutionConfig::for_notarized_transaction(NetworkDefinition::simulator()), - &TestTransaction::new_from_nonce(manifest.clone(), nonce) + Rc::new(TestTransaction::new_from_nonce(manifest.clone(), nonce) .prepare() .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)])), ); receipt.expect_commit_success(); nonce += 1; diff --git a/radix-engine-tests/tests/application/fuzz_transactions.rs b/radix-engine-tests/tests/application/fuzz_transactions.rs index 0110beaf582..9cefd3cbbc9 100644 --- a/radix-engine-tests/tests/application/fuzz_transactions.rs +++ b/radix-engine-tests/tests/application/fuzz_transactions.rs @@ -61,7 +61,7 @@ impl TransactionFuzzer { &mut self.substate_db, vm_init, &execution_config, - &validated.get_executable(), + Rc::new(validated.get_executable()), ); } diff --git a/radix-engine-tests/tests/application/transaction.rs b/radix-engine-tests/tests/application/transaction.rs index 8e30bfd3095..01c18d8f306 100644 --- a/radix-engine-tests/tests/application/transaction.rs +++ b/radix-engine-tests/tests/application/transaction.rs @@ -206,15 +206,15 @@ fn transaction_processor_produces_expected_error_for_undecodable_instructions() let blobs = Default::default(); let executable = Executable::new( - &invalid_encoded_instructions, + Rc::new(invalid_encoded_instructions.to_vec()), &references, - &blobs, + Rc::new(blobs), + Default::default(), ExecutionContext { intent_hash: TransactionIntentHash::NotToCheck { intent_hash: Hash([0; 32]), }, epoch_range: Default::default(), - pre_allocated_addresses: Default::default(), payload_size: 4, num_of_signature_validations: 0, auth_zone_params: Default::default(), diff --git a/radix-engine-tests/tests/kernel/frame.rs b/radix-engine-tests/tests/kernel/frame.rs index 7dfffc6eecf..3edfb696e44 100644 --- a/radix-engine-tests/tests/kernel/frame.rs +++ b/radix-engine-tests/tests/kernel/frame.rs @@ -62,3 +62,153 @@ fn test_max_call_depth_failure() { ) }); } + +#[test] +fn test() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (key, _, account) = ledger.new_allocated_account(); + let (key2, _, account2) = ledger.new_allocated_account(); + let (main_key, _, main_account) = ledger.new_allocated_account(); + let (_, btc) = ledger.create_mintable_burnable_fungible_resource_with_initial_amount(account, Some(Decimal::from(1000))); + let (_, usdc) = ledger.create_mintable_burnable_fungible_resource_with_initial_amount(account2, Some(Decimal::from(1000))); + println!("{:?}", usdc); + + let execution_config = { + let mut execution_config = ExecutionConfig::for_test_transaction(); + execution_config.system_overrides = Some(SystemOverrides { + disable_costing: true, + disable_limits: true, + disable_auth: false, + ..Default::default() + }); + execution_config + }; + + let child_thread0 = { + let mut manifest = ManifestBuilder::new() + .withdraw_from_account(account2, usdc, Decimal::from(23)) + .take_all_from_worktop(usdc, "usdc") + .with_name_lookup(|builder, lookup| { + builder.yield_to_parent(manifest_args!(lookup.bucket("usdc"))) + }) + .assert_worktop_contains(btc, Decimal::from(2)) + .take_all_from_worktop(btc, "btc") + .with_name_lookup(|builder, lookup| { + builder.deposit(account2, lookup.bucket("btc")) + }) + .build(); + + let (instructions, blobs) = manifest.for_intent(); + + let prepared_instructions = instructions.prepare_partial().unwrap(); + let encoded_instructions = manifest_encode(&prepared_instructions.inner.0).unwrap(); + let references = prepared_instructions.references; + let blobs = blobs.prepare_partial().unwrap().blobs_by_hash; + ExecutableThread { + id: Hash([0u8;Hash::LENGTH]), + encoded_instructions: Rc::new(encoded_instructions), + references, + blobs: Rc::new(blobs), + pre_allocated_addresses: vec![], + } + }; + + let child_thread1 = { + let mut manifest = ManifestBuilder::new() + .withdraw_from_account(account, btc, Decimal::from(2)) + .take_all_from_worktop(btc, "btc") + .with_name_lookup(|builder, lookup| { + builder.yield_to_parent(manifest_args!(lookup.bucket("btc"))) + }) + .assert_worktop_contains(usdc, Decimal::from(23)) + .take_all_from_worktop(usdc, "usdc") + .with_name_lookup(|builder, lookup| { + builder.deposit(account, lookup.bucket("usdc")) + }) + .build(); + + let (instructions, blobs) = manifest.for_intent(); + + let prepared_instructions = instructions.prepare_partial().unwrap(); + let encoded_instructions = manifest_encode(&prepared_instructions.inner.0).unwrap(); + let references = prepared_instructions.references; + let blobs = blobs.prepare_partial().unwrap().blobs_by_hash; + ExecutableThread { + id: Hash([1u8;Hash::LENGTH]), + encoded_instructions: Rc::new(encoded_instructions), + references, + blobs: Rc::new(blobs), + pre_allocated_addresses: vec![], + } + }; + + let main_thread = { + let mut manifest = ManifestBuilder::new() + .yield_to_child(Hash([0u8; Hash::LENGTH]), manifest_args!(&())) + .take_all_from_worktop(usdc, "usdc") + .with_name_lookup(|builder, lookup| { + builder.yield_to_child(Hash([1u8; Hash::LENGTH]), manifest_args!(lookup.bucket("usdc"))) + }) + .take_all_from_worktop(btc, "btc") + .with_name_lookup(|builder, lookup| { + builder.yield_to_child(Hash([0u8; Hash::LENGTH]), manifest_args!(lookup.bucket("btc"))) + }) + .deposit_batch(main_account) + .build(); + + let (instructions, blobs) = manifest.for_intent(); + + let prepared_instructions = instructions.prepare_partial().unwrap(); + let encoded_instructions = manifest_encode(&prepared_instructions.inner.0).unwrap(); + let references = prepared_instructions.references; + let blobs = blobs.prepare_partial().unwrap().blobs_by_hash; + ExecutableThread { + id: Hash([2u8;Hash::LENGTH]), + encoded_instructions: Rc::new(encoded_instructions), + references, + blobs: Rc::new(blobs), + pre_allocated_addresses: vec![], + } + }; + + let executable = Executable { + threads: vec![main_thread, child_thread0, child_thread1], + context: ExecutionContext { + intent_hash: TransactionIntentHash::NotToCheck { + intent_hash: Hash([0u8; Hash::LENGTH]) + }, + epoch_range: None, + payload_size: 0usize, + num_of_signature_validations: 1, + auth_zone_params: AuthZoneParams { + thread_params: vec![ + AuthZoneThreadParams { + initial_proofs: btreeset!(NonFungibleGlobalId::from_public_key(&main_key)), + virtual_resources: Default::default(), + }, + AuthZoneThreadParams { + initial_proofs: btreeset!(NonFungibleGlobalId::from_public_key(&key2)), + virtual_resources: Default::default(), + }, + AuthZoneThreadParams { + initial_proofs: btreeset!(NonFungibleGlobalId::from_public_key(&key)), + virtual_resources: Default::default(), + }, + ] + }, + costing_parameters: TransactionCostingParameters { + tip_percentage: DEFAULT_TIP_PERCENTAGE, + free_credit_in_xrd: Decimal::ZERO, + abort_when_loan_repaid: false, + }, + }, + system: false, + }; + + let receipt = ledger.execute_transaction(executable, execution_config); + + // Assert + receipt.expect_commit_success(); +} + diff --git a/radix-engine-tests/tests/kernel/kernel.rs b/radix-engine-tests/tests/kernel/kernel.rs index 744f30eb212..4d6da519f41 100644 --- a/radix-engine-tests/tests/kernel/kernel.rs +++ b/radix-engine-tests/tests/kernel/kernel.rs @@ -1,6 +1,6 @@ use radix_common::prelude::*; use radix_engine::errors::{BootloadingError, CallFrameError, KernelError, RejectionReason, RuntimeError, TransactionExecutionError}; -use radix_engine::kernel::call_frame::{CallFrameMessage, CloseSubstateError, CreateFrameError, CreateNodeError, MovePartitionError, PassMessageError, ProcessSubstateError, StableReferenceType, TakeNodeError, WriteSubstateError}; +use radix_engine::kernel::call_frame::{CallFrameMessage, CloseSubstateError, CreateFrameError, CreateNodeError, MovePartitionError, PassMessageError, ProcessSubstateError, RootCallFrameInitRefs, StableReferenceType, TakeNodeError, WriteSubstateError}; use radix_engine::kernel::id_allocator::IdAllocator; use radix_engine::kernel::kernel::Kernel; use radix_engine::kernel::kernel_api::{ @@ -14,7 +14,8 @@ use radix_engine_interface::prelude::*; use radix_substate_store_impls::memory_db::InMemorySubstateDatabase; use radix_substate_store_interface::db_key_mapper::SpreadPrefixKeyMapper; use radix_substate_store_interface::interface::SubstateDatabase; -use radix_transactions::model::{Executable, PreAllocatedAddress}; +use radix_transactions::model::Executable; +use radix_transactions::prelude::ExecutableThread; struct TestCallFrameData; @@ -61,22 +62,14 @@ impl KernelCallbackObject for TestCallbackObject { fn init( _store: &mut S, - _executable: &Executable, + _executable: Rc, _init_input: Self::Init, - ) -> Result { - Ok(Self) - } - - fn verify_boot_ref_value(&mut self, _node_id: &NodeId, _value: &IndexedScryptoValue) -> Result { - Ok(StableReferenceType::Global) + ) -> Result<(Self, Vec), RejectionReason> { + Ok((Self, vec![])) } fn start>( _api: &mut Y, - _manifest_encoded_instructions: &[u8], - _pre_allocated_addresses: &Vec, - _references: &IndexSet, - _blobs: &IndexMap>, ) -> Result<(), RuntimeError> { unreachable!() } @@ -85,7 +78,7 @@ impl KernelCallbackObject for TestCallbackObject { Ok(()) } - fn create_receipt(self, _track: Track, _executable: &Executable, _result: Result<(), TransactionExecutionError>) -> TestReceipt { + fn create_receipt(self, _track: Track, _result: Result<(), TransactionExecutionError>) -> TestReceipt { TestReceipt } diff --git a/radix-engine-tests/tests/kernel/kernel_open_substate.rs b/radix-engine-tests/tests/kernel/kernel_open_substate.rs index 2dd49f56344..909dc0cd710 100644 --- a/radix-engine-tests/tests/kernel/kernel_open_substate.rs +++ b/radix-engine-tests/tests/kernel/kernel_open_substate.rs @@ -51,8 +51,12 @@ pub fn test_open_substate_of_invisible_package_address() { false, ); + let tx_num_of_signature_validations = + executable.auth_zone_params().thread_params.iter().map(|param| param.initial_proofs.len()).sum(); + // Create kernel let mut system = System { + executable: Rc::new(Executable::mock()), blueprint_cache: NonIterMap::new(), auth_cache: NonIterMap::new(), schema_cache: NonIterMap::new(), @@ -75,7 +79,7 @@ pub fn test_open_substate_of_invisible_package_address() { fee_reserve: SystemLoanFeeReserve::default(), fee_table: FeeTable::new(), tx_payload_len: executable.payload_size(), - tx_num_of_signature_validations: executable.auth_zone_params().initial_proofs.len(), + tx_num_of_signature_validations, config: CostingModuleConfig::babylon_genesis(), cost_breakdown: None, detailed_cost_breakdown: None, diff --git a/radix-engine-tests/tests/kernel/panics.rs b/radix-engine-tests/tests/kernel/panics.rs index f36106aa562..9cfadb7e766 100644 --- a/radix-engine-tests/tests/kernel/panics.rs +++ b/radix-engine-tests/tests/kernel/panics.rs @@ -43,6 +43,20 @@ pub struct MockKernel; impl<'g> KernelApi>> for MockKernel {} +impl<'g> KernelThreadApi>> for MockKernel { + fn kernel_send(&mut self, _: usize, _: IndexedScryptoValue) -> Result<(), RuntimeError> { + panic!() + } + + fn kernel_switch_context(&mut self, _: usize) -> Result<(), RuntimeError> { + panic1!() + } + + fn kernel_update_call_frame_data(&mut self, _: Actor) -> Result<(), RuntimeError> { + panic!() + } +} + impl KernelNodeApi for MockKernel { fn kernel_pin_node(&mut self, _: NodeId) -> Result<(), RuntimeError> { panic1!() @@ -181,6 +195,10 @@ impl<'g> KernelInternalApi>> for M panic1!() } + fn kernel_get_current_thread(&self) -> usize { + panic1!() + } + fn kernel_get_node_visibility(&self, _: &NodeId) -> NodeVisibility { panic1!() } diff --git a/radix-engine-tests/tests/kernel/transaction_executor.rs b/radix-engine-tests/tests/kernel/transaction_executor.rs index 66df7e2acac..ecf64524d0e 100644 --- a/radix-engine-tests/tests/kernel/transaction_executor.rs +++ b/radix-engine-tests/tests/kernel/transaction_executor.rs @@ -138,7 +138,7 @@ fn test_normal_transaction_flow() { &mut substate_db, vm_init, &execution_config, - &executable, + Rc::new(executable), ); // Assert diff --git a/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs b/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs index e73667145bb..195ad317fbf 100644 --- a/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs +++ b/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs @@ -57,12 +57,12 @@ mod multi_threaded_test { &mut substate_db, vm_init.clone(), &ExecutionConfig::for_test_transaction(), - &TestTransaction::new(manifest, hash(format!("Account creation: {i}"))) + Rc::new(TestTransaction::new(manifest, hash(format!("Account creation: {i}"))) .prepare() .unwrap() .get_executable(btreeset![NonFungibleGlobalId::from_public_key( &public_key - )]), + )])), ) .expect_commit(true) .new_component_addresses()[0]; @@ -84,10 +84,10 @@ mod multi_threaded_test { &mut substate_db, vm_init.clone(), &ExecutionConfig::for_test_transaction(), - &TestTransaction::new(manifest.clone(), hash(format!("Fill account: {}", nonce))) + Rc::new(TestTransaction::new(manifest.clone(), hash(format!("Fill account: {}", nonce))) .prepare() .expect("Expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)])), ) .expect_commit(true); } @@ -109,12 +109,12 @@ mod multi_threaded_test { &substate_db, vm_init.clone(), &ExecutionConfig::for_test_transaction(), - &TestTransaction::new(manifest.clone(), hash(format!("Transfer"))) + Rc::new(TestTransaction::new(manifest.clone(), hash(format!("Transfer"))) .prepare() .expect("Expected transaction to be preparable") .get_executable(btreeset![NonFungibleGlobalId::from_public_key( &public_key, - )]), + )])), ); receipt.expect_commit_success(); println!("receipt = {:?}", receipt); diff --git a/radix-engine-tests/tests/vm/native_vm.rs b/radix-engine-tests/tests/vm/native_vm.rs index 1be5d96319a..669b3b500d2 100644 --- a/radix-engine-tests/tests/vm/native_vm.rs +++ b/radix-engine-tests/tests/vm/native_vm.rs @@ -73,6 +73,7 @@ fn panics_can_be_caught_in_the_native_vm_and_converted_into_results() { let intent_hash = Hash([0; 32]); let mut system = System { + executable: Rc::new(Executable::mock()), blueprint_cache: NonIterMap::new(), auth_cache: NonIterMap::new(), schema_cache: NonIterMap::new(), @@ -85,10 +86,10 @@ fn panics_can_be_caught_in_the_native_vm_and_converted_into_results() { EnabledModules::for_notarized_transaction(), KernelTraceModule, TransactionRuntimeModule::new(NetworkDefinition::simulator(), intent_hash), - AuthModule::new(AuthZoneParams { - initial_proofs: Default::default(), - virtual_resources: Default::default(), - }), + AuthModule::new(AuthZoneParams::single_thread( + Default::default(), + Default::default(), + )), LimitsModule::babylon_genesis(), CostingModule { current_depth: 0, @@ -154,6 +155,7 @@ fn any_panics_can_be_caught_in_the_native_vm_and_converted_into_results() { let intent_hash = Hash([0; 32]); let mut system = System { + executable: Rc::new(Executable::mock()), blueprint_cache: NonIterMap::new(), auth_cache: NonIterMap::new(), schema_cache: NonIterMap::new(), @@ -166,10 +168,10 @@ fn any_panics_can_be_caught_in_the_native_vm_and_converted_into_results() { EnabledModules::for_notarized_transaction(), KernelTraceModule, TransactionRuntimeModule::new(NetworkDefinition::simulator(), intent_hash), - AuthModule::new(AuthZoneParams { - initial_proofs: Default::default(), - virtual_resources: Default::default(), - }), + AuthModule::new(AuthZoneParams::single_thread( + Default::default(), + Default::default(), + )), LimitsModule::babylon_genesis(), CostingModule { current_depth: 0, diff --git a/radix-engine/src/blueprints/resource/worktop.rs b/radix-engine/src/blueprints/resource/worktop.rs index 9ac0c21a3e0..015b2b0b450 100644 --- a/radix-engine/src/blueprints/resource/worktop.rs +++ b/radix-engine/src/blueprints/resource/worktop.rs @@ -25,6 +25,7 @@ impl WorktopSubstate { #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] pub enum WorktopError { AssertionFailed, + AssertAmountFailed(Decimal), InsufficientBalance, } @@ -277,7 +278,7 @@ impl WorktopBlueprint { }; if amount < input.amount { return Err(RuntimeError::ApplicationError( - ApplicationError::WorktopError(WorktopError::AssertionFailed), + ApplicationError::WorktopError(WorktopError::AssertAmountFailed(input.amount)), )); } api.field_close(worktop_handle)?; diff --git a/radix-engine/src/blueprints/transaction_processor/package.rs b/radix-engine/src/blueprints/transaction_processor/package.rs index 9d3182c3784..1ddbfe40b6b 100644 --- a/radix-engine/src/blueprints/transaction_processor/package.rs +++ b/radix-engine/src/blueprints/transaction_processor/package.rs @@ -14,7 +14,7 @@ use radix_engine_interface::blueprints::package::{ }; use radix_engine_interface::blueprints::transaction_processor::*; -use super::TransactionProcessorBlueprint; +use super::{TransactionProcessorBlueprint}; use super::TransactionProcessorRunInput; use super::TransactionProcessorV1MinorVersion; @@ -90,16 +90,13 @@ impl TransactionProcessorNativePackage { RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e)) })?; - let rtn = TransactionProcessorBlueprint::run( - input.manifest_encoded_instructions, - input.global_address_reservations, - input.references, - input.blobs, + let result = TransactionProcessorBlueprint::run( + input.threads, version, api, )?; - Ok(IndexedScryptoValue::from_typed(&rtn)) + Ok(IndexedScryptoValue::from_typed(&result)) } _ => Err(RuntimeError::ApplicationError( ApplicationError::ExportDoesNotExist(export_name.to_string()), diff --git a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs b/radix-engine/src/blueprints/transaction_processor/tx_processor.rs index 65ae733ee54..9aa7908ef1a 100644 --- a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs +++ b/radix-engine/src/blueprints/transaction_processor/tx_processor.rs @@ -20,6 +20,7 @@ use radix_transactions::data::TransformHandler; use radix_transactions::model::*; use radix_transactions::validation::*; use sbor::rust::prelude::*; +use crate::vm::NativeVmInvokeResult; #[cfg(not(feature = "coverage"))] pub const MAX_TOTAL_BLOB_SIZE_PER_INVOCATION: usize = 1024 * 1024; @@ -34,20 +35,32 @@ pub enum TransactionProcessorV1MinorVersion { } #[derive(Debug, Eq, PartialEq, ScryptoSbor)] -pub struct TransactionProcessorRunInput { +pub struct TransactionProcessorThreadRunInput { + pub id: Hash, pub manifest_encoded_instructions: Vec, pub global_address_reservations: Vec, pub references: Vec, // Required so that the kernel passes the references to the processor frame pub blobs: IndexMap>, } +#[derive(Debug, Eq, PartialEq, ScryptoSbor)] +pub struct TransactionProcessorRunInput { + pub threads: Vec, +} + // This needs to match the above, but is easily encodable to avoid cloning from the transaction payload to encode -#[derive(Debug, Eq, PartialEq, ScryptoEncode)] -pub struct TransactionProcessorRunInputEfficientEncodable<'a> { - pub manifest_encoded_instructions: &'a [u8], +#[derive(Debug, Eq, PartialEq, ScryptoSbor)] +pub struct TransactionProcessorThreadRunInputEfficientEncodable { + pub id: Hash, + pub manifest_encoded_instructions: Rc>, pub global_address_reservations: Vec, - pub references: &'a IndexSet, - pub blobs: &'a IndexMap>, + pub references: IndexSet, + pub blobs: Rc>>, +} + +#[derive(Debug, Eq, PartialEq, ScryptoSbor)] +pub struct TransactionProcessorRunInputEfficientEncodable { + pub threads: Vec, } #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] @@ -103,6 +116,7 @@ fn handle_invocation<'a, 'p, 'w, Y: SystemApi + KernelSubstateApi< Ok(InstructionOutput::CallReturn(result.into())) } + pub struct TransactionProcessorBlueprint; impl TransactionProcessorBlueprint { @@ -110,364 +124,513 @@ impl TransactionProcessorBlueprint { Y: SystemApi + KernelNodeApi + KernelSubstateApi, L: Default, >( - manifest_encoded_instructions: Vec, - global_address_reservations: Vec, - _references: Vec, // Required so that the kernel passes the references to the processor frame - blobs: IndexMap>, + thread_inputs: Vec, version: TransactionProcessorV1MinorVersion, api: &mut Y, ) -> Result, RuntimeError> { - // Create a worktop - let worktop_node_id = api.kernel_allocate_node_id(EntityType::InternalGenericComponent)?; - api.kernel_create_node( - worktop_node_id, - btreemap!( - MAIN_BASE_PARTITION => btreemap!( - WorktopField::Worktop.into() => IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(WorktopSubstate::new())) - ), - TYPE_INFO_FIELD_PARTITION => type_info_partition( - TypeInfoSubstate::Object(ObjectInfo { - blueprint_info: BlueprintInfo { - blueprint_id: BlueprintId::new(&RESOURCE_PACKAGE, WORKTOP_BLUEPRINT), - blueprint_version: BlueprintVersion::default(), - generic_substitutions: Vec::new(), - outer_obj_info: OuterObjectInfo::default(), - features: indexset!(), - }, - object_type: ObjectType::Owned, - }) - ) - ), - )?; - api.kernel_pin_node(worktop_node_id)?; - - let mut worktop = Worktop(Own(worktop_node_id)); - let instructions = manifest_decode::>(&manifest_encoded_instructions) - .map_err(|e| { - // This error should never occur if being called from root since this is constructed - // by the transaction executor. This error is more to protect against application - // space calling this function if/when possible - RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e)) - })?; - let mut processor = TransactionProcessor::new(blobs, global_address_reservations); + let mut threads: Vec<_> = thread_inputs.into_iter() + .map(|input| (input.id, TransactionProcessorThread::Uninitialized(input))) + .collect(); + + let hash_to_index: NonIterMap = threads.iter().enumerate().map(|(index, (id, ..))| { + (*id, index) + }).collect(); + let mut outputs = Vec::new(); - for (index, inst) in instructions.into_iter().enumerate() { - api.update_instruction_index(index)?; - - let result = match inst { - InstructionV1::TakeAllFromWorktop { resource_address } => { - let bucket = worktop.take_all(resource_address, api)?; - processor.create_manifest_bucket(bucket)?; - InstructionOutput::None - } - InstructionV1::TakeFromWorktop { - amount, - resource_address, - } => { - let bucket = worktop.take(resource_address, amount, api)?; - processor.create_manifest_bucket(bucket)?; - InstructionOutput::None - } - InstructionV1::TakeNonFungiblesFromWorktop { - ids, - resource_address, - } => { - let bucket = worktop.take_non_fungibles( - resource_address, - ids.into_iter().collect(), - api, - )?; - processor.create_manifest_bucket(bucket)?; - InstructionOutput::None - } - InstructionV1::ReturnToWorktop { bucket_id } => { - let bucket = processor.take_bucket(&bucket_id)?; - worktop.put(bucket, api)?; - InstructionOutput::None - } - InstructionV1::AssertWorktopContainsAny { resource_address } => { - worktop.assert_contains(resource_address, api)?; - InstructionOutput::None - } - InstructionV1::AssertWorktopContains { - amount, - resource_address, - } => { - worktop.assert_contains_amount(resource_address, amount, api)?; - InstructionOutput::None + let mut cur_thread = 0usize; + let mut sent_value = None; + + loop { + let (_, ref mut thread) = threads.get_mut(cur_thread).unwrap(); + let result = thread.execute(api, sent_value)?; + sent_value = None; + match result { + InstructionExecutionResult::Output(output) => outputs.push(output), + InstructionExecutionResult::Done => { + if cur_thread > 0 { + api.free_stack(0)?; + } + + let next = threads.iter().enumerate() + .filter(|(thread_id, (_, thread))| !matches!(thread, TransactionProcessorThread::Done)) + .next(); + if let Some((thread_id, ..)) = next { + cur_thread = thread_id; + api.switch_stack(thread_id)?; + } else { + api.switch_stack(0)?; + return Ok(outputs); + } + }, + InstructionExecutionResult::YieldToChild(child, value) => { + println!("YIELD_TO_CHILD"); + let thread = *hash_to_index.get(&child).unwrap(); + cur_thread = thread; + api.move_to_stack(thread, value.clone())?; + api.switch_stack(thread)?; + sent_value = Some(value); } - InstructionV1::AssertWorktopContainsNonFungibles { - ids, - resource_address, - } => { - worktop.assert_contains_non_fungibles( - resource_address, - ids.into_iter().collect(), - api, - )?; - InstructionOutput::None + InstructionExecutionResult::YieldToParent(value) => { + println!("YIELD_TO_PARENT"); + cur_thread = 0; + api.move_to_stack(0, value.clone())?; + api.switch_stack(0)?; + sent_value = Some(value); } - InstructionV1::PopFromAuthZone {} => { - let proof = LocalAuthZone::pop(api)?.ok_or(RuntimeError::ApplicationError( - ApplicationError::TransactionProcessorError( - TransactionProcessorError::AuthZoneIsEmpty, + } + } + } +} + +pub enum InstructionExecutionResult { + Output(InstructionOutput), + YieldToChild(Hash, IndexedScryptoValue), + YieldToParent(IndexedScryptoValue), + Done, +} + + +pub enum TransactionProcessorThread { + Uninitialized(TransactionProcessorThreadRunInput), + Running(TransactionProcessorRunningState), + Done +} + +pub struct TransactionProcessorRunningState { + index: usize, + worktop: Worktop, + instructions: Vec, + processor: TransactionProcessor, + version: TransactionProcessorV1MinorVersion, +} + +impl TransactionProcessorThread { + fn execute< + Y: SystemApi + KernelNodeApi + KernelSubstateApi, + L: Default, + >(&mut self, api: &mut Y, mut next_value: Option) -> Result { + loop { + match self { + TransactionProcessorThread::Uninitialized(input) => { + // Create a worktop + let worktop_node_id = api.kernel_allocate_node_id(EntityType::InternalGenericComponent)?; + api.kernel_create_node( + worktop_node_id, + btreemap!( + MAIN_BASE_PARTITION => btreemap!( + WorktopField::Worktop.into() => IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(WorktopSubstate::new())) + ), + TYPE_INFO_FIELD_PARTITION => type_info_partition( + TypeInfoSubstate::Object(ObjectInfo { + blueprint_info: BlueprintInfo { + blueprint_id: BlueprintId::new(&RESOURCE_PACKAGE, WORKTOP_BLUEPRINT), + blueprint_version: BlueprintVersion::default(), + generic_substitutions: Vec::new(), + outer_obj_info: OuterObjectInfo::default(), + features: indexset!(), + }, + object_type: ObjectType::Owned, + }) + ) ), - ))?; - processor.create_manifest_proof(proof)?; - InstructionOutput::None - } - InstructionV1::PushToAuthZone { proof_id } => { - let proof = processor.take_proof(&proof_id)?; - LocalAuthZone::push(proof, api)?; - InstructionOutput::None - } - InstructionV1::CreateProofFromAuthZoneOfAmount { - amount, - resource_address, - } => { - let proof = - LocalAuthZone::create_proof_of_amount(amount, resource_address, api)?; - processor.create_manifest_proof(proof)?; - InstructionOutput::None - } - InstructionV1::CreateProofFromAuthZoneOfNonFungibles { - ids, - resource_address, - } => { - let proof = LocalAuthZone::create_proof_of_non_fungibles( - &ids.into_iter().collect(), - resource_address, - api, )?; - processor.create_manifest_proof(proof)?; - InstructionOutput::None - } - InstructionV1::CreateProofFromAuthZoneOfAll { resource_address } => { - let proof = LocalAuthZone::create_proof_of_all(resource_address, api)?; - processor.create_manifest_proof(proof)?; - InstructionOutput::None - } - InstructionV1::CreateProofFromBucketOfAmount { bucket_id, amount } => { - let bucket = processor.get_bucket(&bucket_id)?; - let proof = bucket.create_proof_of_amount(amount, api)?; - processor.create_manifest_proof(proof.into())?; - InstructionOutput::None - } - InstructionV1::CreateProofFromBucketOfNonFungibles { bucket_id, ids } => { - let bucket = processor.get_bucket(&bucket_id)?; - let proof = - bucket.create_proof_of_non_fungibles(ids.into_iter().collect(), api)?; - processor.create_manifest_proof(proof.into())?; - InstructionOutput::None - } - InstructionV1::CreateProofFromBucketOfAll { bucket_id } => { - let bucket = processor.get_bucket(&bucket_id)?; - let proof = bucket.create_proof_of_all(api)?; - processor.create_manifest_proof(proof)?; - InstructionOutput::None - } - InstructionV1::DropAuthZoneProofs => { - LocalAuthZone::drop_proofs(api)?; - InstructionOutput::None - } - InstructionV1::DropAuthZoneRegularProofs => { - LocalAuthZone::drop_regular_proofs(api)?; - InstructionOutput::None - } - InstructionV1::DropAuthZoneSignatureProofs => { - LocalAuthZone::drop_signature_proofs(api)?; - InstructionOutput::None - } - InstructionV1::BurnResource { bucket_id } => { - let bucket = processor.take_bucket(&bucket_id)?; - let rtn = bucket.burn(api)?; + api.kernel_pin_node(worktop_node_id)?; + + let worktop = Worktop(Own(worktop_node_id)); + let instructions = manifest_decode::>(&input.manifest_encoded_instructions) + .map_err(|e| { + // This error should never occur if being called from root since this is constructed + // by the transaction executor. This error is more to protect against application + // space calling this function if/when possible + RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e)) + })?; + let mut processor = TransactionProcessor::new(input.blobs.clone(), input.global_address_reservations.clone()); + + if let Some(value) = next_value.take() { + processor.handle_call_return_data(&value, &worktop, api)?; + } - let result = IndexedScryptoValue::from_typed(&rtn); - processor.handle_call_return_data(&result, &worktop, api)?; - InstructionOutput::CallReturn(result.into()) - } - InstructionV1::CloneProof { proof_id } => { - let proof = processor.get_proof(&proof_id)?; - let proof = proof.clone(api)?; - processor.create_manifest_proof(proof)?; - InstructionOutput::None - } - InstructionV1::DropProof { proof_id } => { - let proof = processor.take_proof(&proof_id)?; - proof.drop(api)?; - InstructionOutput::None - } - InstructionV1::CallFunction { - package_address, - blueprint_name, - function_name, - args, - } => { - let package_address = processor.resolve_package_address(package_address)?; - handle_invocation( - api, - &mut processor, - &mut worktop, - args, - |api, args| { - api.call_function( - package_address, - &blueprint_name, - &function_name, - scrypto_encode(&args) - .map_err(TransactionProcessorError::ArgsEncodeError)?, - ) - }, - version, - )? - } - InstructionV1::CallMethod { - address, - method_name, - args, - } => { - let address = processor.resolve_global_address(address)?; - handle_invocation( - api, - &mut processor, - &mut worktop, - args, - |api, args| { - api.call_method( - address.as_node_id(), - &method_name, - scrypto_encode(&args) - .map_err(TransactionProcessorError::ArgsEncodeError)?, - ) - }, - version, - )? - } - InstructionV1::CallRoyaltyMethod { - address, - method_name, - args, - } => { - let address = processor.resolve_global_address(address)?; - handle_invocation( - api, - &mut processor, - &mut worktop, - args, - |api, args| { - api.call_module_method( - address.as_node_id(), - AttachedModuleId::Royalty, - &method_name, - scrypto_encode(&args) - .map_err(TransactionProcessorError::ArgsEncodeError)?, - ) - }, - version, - )? + *self = TransactionProcessorThread::Running(TransactionProcessorRunningState { + worktop, + index: 0, + instructions, + processor, + version: TransactionProcessorV1MinorVersion::One + }); } - InstructionV1::CallMetadataMethod { - address, - method_name, - args, - } => { - let address = processor.resolve_global_address(address)?; - handle_invocation( - api, - &mut processor, - &mut worktop, - args, - |api, args| { - api.call_module_method( - address.as_node_id(), - AttachedModuleId::Metadata, - &method_name, - scrypto_encode(&args) - .map_err(TransactionProcessorError::ArgsEncodeError)?, - ) - }, - version, - )? - } - InstructionV1::CallRoleAssignmentMethod { - address, - method_name, - args, - } => { - let address = processor.resolve_global_address(address)?; - handle_invocation( - api, - &mut processor, - &mut worktop, - args, - |api, args| { - api.call_module_method( - address.as_node_id(), - AttachedModuleId::RoleAssignment, - &method_name, - scrypto_encode(&args) - .map_err(TransactionProcessorError::ArgsEncodeError)?, - ) - }, - version, - )? - } - InstructionV1::CallDirectVaultMethod { - address, - method_name, - args, - } => handle_invocation( - api, - &mut processor, - &mut worktop, - args, - |api, args| { - api.call_direct_access_method( - address.as_node_id(), - &method_name, - scrypto_encode(&args) - .map_err(TransactionProcessorError::ArgsEncodeError)?, - ) - }, - version, - )?, - InstructionV1::DropNamedProofs => { - for (_, real_id) in processor.proof_mapping.drain(..) { - let proof = Proof(Own(real_id)); - proof.drop(api).map(|_| IndexedScryptoValue::unit())?; + TransactionProcessorThread::Running(state) => { + let mut worktop = state.worktop; + let version = state.version; + + if let Some(value) = next_value.take() { + state.processor.handle_call_return_data(&value, &worktop, api)?; } - InstructionOutput::None - } - InstructionV1::DropAllProofs => { - for (_, real_id) in processor.proof_mapping.drain(..) { - let proof = Proof(Own(real_id)); - proof.drop(api).map(|_| IndexedScryptoValue::unit())?; + + if state.index >= state.instructions.len() { + worktop.drop(api)?; + *self = TransactionProcessorThread::Done; + return Ok(InstructionExecutionResult::Done); } - LocalAuthZone::drop_proofs(api)?; - InstructionOutput::None - } - InstructionV1::AllocateGlobalAddress { - package_address, - blueprint_name, - } => { - let (address_reservation, address) = api.allocate_global_address( - BlueprintId::new(&package_address, blueprint_name), - )?; - processor.create_manifest_address_reservation(address_reservation)?; - processor.create_manifest_address(address)?; - InstructionOutput::None + api.update_instruction_index(state.index)?; + let inst = state.instructions.get(state.index).unwrap().clone(); + + state.index += 1; + + let output = match inst { + InstructionV1::TakeAllFromWorktop { resource_address } => { + let bucket = worktop.take_all(resource_address, api)?; + state.processor.create_manifest_bucket(bucket)?; + InstructionOutput::None + } + InstructionV1::TakeFromWorktop { + amount, + resource_address, + } => { + let bucket = worktop.take(resource_address, amount, api)?; + state.processor.create_manifest_bucket(bucket)?; + InstructionOutput::None + } + InstructionV1::TakeNonFungiblesFromWorktop { + ids, + resource_address, + } => { + let bucket = worktop.take_non_fungibles( + resource_address, + ids.into_iter().collect(), + api, + )?; + state.processor.create_manifest_bucket(bucket)?; + InstructionOutput::None + } + InstructionV1::ReturnToWorktop { bucket_id } => { + let bucket = state.processor.take_bucket(&bucket_id)?; + worktop.put(bucket, api)?; + InstructionOutput::None + } + InstructionV1::AssertWorktopContainsAny { resource_address } => { + worktop.assert_contains(resource_address, api)?; + InstructionOutput::None + } + InstructionV1::AssertWorktopContains { + amount, + resource_address, + } => { + worktop.assert_contains_amount(resource_address, amount, api)?; + InstructionOutput::None + } + InstructionV1::AssertWorktopContainsNonFungibles { + ids, + resource_address, + } => { + worktop.assert_contains_non_fungibles( + resource_address, + ids.into_iter().collect(), + api, + )?; + InstructionOutput::None + } + InstructionV1::PopFromAuthZone {} => { + let proof = LocalAuthZone::pop(api)?.ok_or(RuntimeError::ApplicationError( + ApplicationError::TransactionProcessorError( + TransactionProcessorError::AuthZoneIsEmpty, + ), + ))?; + state.processor.create_manifest_proof(proof)?; + InstructionOutput::None + } + InstructionV1::PushToAuthZone { proof_id } => { + let proof = state.processor.take_proof(&proof_id)?; + LocalAuthZone::push(proof, api)?; + InstructionOutput::None + } + InstructionV1::CreateProofFromAuthZoneOfAmount { + amount, + resource_address, + } => { + let proof = + LocalAuthZone::create_proof_of_amount(amount, resource_address, api)?; + state.processor.create_manifest_proof(proof)?; + InstructionOutput::None + } + InstructionV1::CreateProofFromAuthZoneOfNonFungibles { + ids, + resource_address, + } => { + let proof = LocalAuthZone::create_proof_of_non_fungibles( + &ids.into_iter().collect(), + resource_address, + api, + )?; + state.processor.create_manifest_proof(proof)?; + InstructionOutput::None + } + InstructionV1::CreateProofFromAuthZoneOfAll { resource_address } => { + let proof = LocalAuthZone::create_proof_of_all(resource_address, api)?; + state.processor.create_manifest_proof(proof)?; + InstructionOutput::None + } + InstructionV1::CreateProofFromBucketOfAmount { bucket_id, amount } => { + let bucket = state.processor.get_bucket(&bucket_id)?; + let proof = bucket.create_proof_of_amount(amount, api)?; + state.processor.create_manifest_proof(proof.into())?; + InstructionOutput::None + } + InstructionV1::CreateProofFromBucketOfNonFungibles { bucket_id, ids } => { + let bucket = state.processor.get_bucket(&bucket_id)?; + let proof = + bucket.create_proof_of_non_fungibles(ids.into_iter().collect(), api)?; + state.processor.create_manifest_proof(proof.into())?; + InstructionOutput::None + } + InstructionV1::CreateProofFromBucketOfAll { bucket_id } => { + let bucket = state.processor.get_bucket(&bucket_id)?; + let proof = bucket.create_proof_of_all(api)?; + state.processor.create_manifest_proof(proof)?; + InstructionOutput::None + } + InstructionV1::DropAuthZoneProofs => { + LocalAuthZone::drop_proofs(api)?; + InstructionOutput::None + } + InstructionV1::DropAuthZoneRegularProofs => { + LocalAuthZone::drop_regular_proofs(api)?; + InstructionOutput::None + } + InstructionV1::DropAuthZoneSignatureProofs => { + LocalAuthZone::drop_signature_proofs(api)?; + InstructionOutput::None + } + InstructionV1::BurnResource { bucket_id } => { + let bucket = state.processor.take_bucket(&bucket_id)?; + let rtn = bucket.burn(api)?; + + let result = IndexedScryptoValue::from_typed(&rtn); + state.processor.handle_call_return_data(&result, &worktop, api)?; + InstructionOutput::CallReturn(result.into()) + } + InstructionV1::CloneProof { proof_id } => { + let proof = state.processor.get_proof(&proof_id)?; + let proof = proof.clone(api)?; + state.processor.create_manifest_proof(proof)?; + InstructionOutput::None + } + InstructionV1::DropProof { proof_id } => { + let proof = state.processor.take_proof(&proof_id)?; + proof.drop(api)?; + InstructionOutput::None + } + InstructionV1::CallFunction { + package_address, + blueprint_name, + function_name, + args, + } => { + let package_address = state.processor.resolve_package_address(package_address)?; + handle_invocation( + api, + &mut state.processor, + &mut worktop, + args, + |api, args| { + api.call_function( + package_address, + &blueprint_name, + &function_name, + scrypto_encode(&args) + .map_err(TransactionProcessorError::ArgsEncodeError)?, + ) + }, + version, + )? + } + InstructionV1::CallMethod { + address, + method_name, + args, + } => { + let address = state.processor.resolve_global_address(address)?; + handle_invocation( + api, + &mut state.processor, + &mut worktop, + args, + |api, args| { + api.call_method( + address.as_node_id(), + &method_name, + scrypto_encode(&args) + .map_err(TransactionProcessorError::ArgsEncodeError)?, + ) + }, + version, + )? + } + InstructionV1::CallRoyaltyMethod { + address, + method_name, + args, + } => { + let address = state.processor.resolve_global_address(address)?; + handle_invocation( + api, + &mut state.processor, + &mut worktop, + args, + |api, args| { + api.call_module_method( + address.as_node_id(), + AttachedModuleId::Royalty, + &method_name, + scrypto_encode(&args) + .map_err(TransactionProcessorError::ArgsEncodeError)?, + ) + }, + version, + )? + } + InstructionV1::CallMetadataMethod { + address, + method_name, + args, + } => { + let address = state.processor.resolve_global_address(address)?; + handle_invocation( + api, + &mut state.processor, + &mut worktop, + args, + |api, args| { + api.call_module_method( + address.as_node_id(), + AttachedModuleId::Metadata, + &method_name, + scrypto_encode(&args) + .map_err(TransactionProcessorError::ArgsEncodeError)?, + ) + }, + version, + )? + } + InstructionV1::CallRoleAssignmentMethod { + address, + method_name, + args, + } => { + let address = state.processor.resolve_global_address(address)?; + handle_invocation( + api, + &mut state.processor, + &mut worktop, + args, + |api, args| { + api.call_module_method( + address.as_node_id(), + AttachedModuleId::RoleAssignment, + &method_name, + scrypto_encode(&args) + .map_err(TransactionProcessorError::ArgsEncodeError)?, + ) + }, + version, + )? + } + InstructionV1::CallDirectVaultMethod { + address, + method_name, + args, + } => handle_invocation( + api, + &mut state.processor, + &mut worktop, + args, + |api, args| { + api.call_direct_access_method( + address.as_node_id(), + &method_name, + scrypto_encode(&args) + .map_err(TransactionProcessorError::ArgsEncodeError)?, + ) + }, + version, + )?, + InstructionV1::DropNamedProofs => { + for (_, real_id) in state.processor.proof_mapping.drain(..) { + let proof = Proof(Own(real_id)); + proof.drop(api).map(|_| IndexedScryptoValue::unit())?; + } + InstructionOutput::None + } + InstructionV1::DropAllProofs => { + for (_, real_id) in state.processor.proof_mapping.drain(..) { + let proof = Proof(Own(real_id)); + proof.drop(api).map(|_| IndexedScryptoValue::unit())?; + } + LocalAuthZone::drop_proofs(api)?; + InstructionOutput::None + } + InstructionV1::AllocateGlobalAddress { + package_address, + blueprint_name, + } => { + let (address_reservation, address) = api.allocate_global_address( + BlueprintId::new(&package_address, blueprint_name), + )?; + state.processor.create_manifest_address_reservation(address_reservation)?; + state.processor.create_manifest_address(address)?; + + InstructionOutput::None + } + InstructionV1::YieldToChild { + id, + args + } => { + let scrypto_value = { + let mut processor_with_api = TransactionProcessorWithApi { + worktop: &mut worktop, + processor: &mut state.processor, + api, + current_total_size_of_blobs: 0, + max_total_size_of_blobs: match version { + TransactionProcessorV1MinorVersion::Zero => usize::MAX, + TransactionProcessorV1MinorVersion::One => MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, + }, + }; + transform(args, &mut processor_with_api)? + }; + return Ok(InstructionExecutionResult::YieldToChild(id, IndexedScryptoValue::from_scrypto_value(scrypto_value))); + } + InstructionV1::YieldToParent { + args + } => { + let scrypto_value = { + let mut processor_with_api = TransactionProcessorWithApi { + worktop: &mut worktop, + processor: &mut state.processor, + api, + current_total_size_of_blobs: 0, + max_total_size_of_blobs: match version { + TransactionProcessorV1MinorVersion::Zero => usize::MAX, + TransactionProcessorV1MinorVersion::One => MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, + }, + }; + transform(args, &mut processor_with_api)? + }; + return Ok(InstructionExecutionResult::YieldToParent(IndexedScryptoValue::from_scrypto_value(scrypto_value))); + } + }; + + return Ok(InstructionExecutionResult::Output(output)); + } + TransactionProcessorThread::Done => { + panic!("Already done"); } - }; - outputs.push(result); + } } - - worktop.drop(api)?; - - Ok(outputs) } } + struct TransactionProcessor { bucket_mapping: NonIterMap, proof_mapping: IndexMap, diff --git a/radix-engine/src/kernel/call_frame.rs b/radix-engine/src/kernel/call_frame.rs index df82accc5f0..d0315315e5a 100644 --- a/radix-engine/src/kernel/call_frame.rs +++ b/radix-engine/src/kernel/call_frame.rs @@ -557,9 +557,15 @@ pub enum SubstateDiffError { ContainsDuplicateOwns, } +#[derive(Debug, Default)] +pub struct RootCallFrameInitRefs { + pub global_addresses: IndexSet, + pub direct_accesses: IndexSet, +} + impl CallFrame { - pub fn new_root(call_frame_data: C) -> Self { - Self { + pub fn new_root(call_frame_data: C, root_refs: RootCallFrameInitRefs) -> Self { + let mut call_frame = Self { depth: 0, call_frame_data, stable_references: Default::default(), @@ -567,7 +573,16 @@ impl CallFrame { owned_root_nodes: index_set_new(), next_handle: 0u32, open_substates: index_map_new(), + }; + + for global_ref in root_refs.global_addresses { + call_frame.add_global_reference(global_ref); } + for direct_access in root_refs.direct_accesses { + call_frame.add_direct_access_reference(direct_access); + } + + call_frame } pub fn new_child_from_parent( @@ -588,7 +603,10 @@ impl CallFrame { // Copy references and move nodes Self::pass_message(substate_io, parent, &mut frame, message) - .map_err(CreateFrameError::PassMessageError)?; + .map_err(CreateFrameError::PassMessageError).map_err(|e| { + println!("HEre"); + e + })?; Ok(frame) } @@ -663,6 +681,10 @@ impl CallFrame { &self.call_frame_data } + pub fn data_mut(&mut self) -> &mut C { + &mut self.call_frame_data + } + pub fn pin_node<'f, S: CommitableSubstateStore>( &mut self, substate_io: &mut SubstateIO, diff --git a/radix-engine/src/kernel/kernel.rs b/radix-engine/src/kernel/kernel.rs index 4f2c5583abf..1900172639d 100644 --- a/radix-engine/src/kernel/kernel.rs +++ b/radix-engine/src/kernel/kernel.rs @@ -1,4 +1,6 @@ -use super::call_frame::{CallFrame, NodeVisibility, OpenSubstateError, StableReferenceType}; +use super::call_frame::{ + CallFrame, NodeVisibility, OpenSubstateError, RootCallFrameInitRefs, StableReferenceType, +}; use super::heap::Heap; use super::id_allocator::IdAllocator; use crate::blueprints::resource::*; @@ -29,7 +31,7 @@ use radix_engine_interface::blueprints::resource::*; use radix_engine_profiling_derive::trace_resources; use radix_substate_store_interface::db_key_mapper::{SpreadPrefixKeyMapper, SubstateKeyContent}; use radix_substate_store_interface::interface::SubstateDatabase; -use radix_transactions::prelude::Executable; +use radix_transactions::prelude::{Executable, ExecutableThread}; use sbor::rust::mem; pub const BOOT_LOADER_KERNEL_BOOT_FIELD_KEY: FieldKey = 0u8; @@ -57,7 +59,7 @@ pub struct BootLoader<'h, M: KernelCallbackObject, S: SubstateDatabase> { impl<'h, M: KernelCallbackObject, S: SubstateDatabase> BootLoader<'h, M, S> { /// Executes a transaction - pub fn execute<'a>(self, executable: &Executable) -> M::Receipt { + pub fn execute(self, executable: Rc) -> M::Receipt { // Start hardware resource usage tracker #[cfg(all(target_os = "linux", feature = "std", feature = "cpu_ram_metrics"))] let mut resources_tracker = @@ -65,8 +67,9 @@ impl<'h, M: KernelCallbackObject, S: SubstateDatabase> BootLoader<'h, M, S> { #[cfg(not(all(target_os = "linux", feature = "std", feature = "cpu_ram_metrics")))] { + let cloned = executable.clone(); self.execute_internal(executable) - .unwrap_or_else(|reason| M::Receipt::from_rejection(executable, reason)) + .unwrap_or_else(|reason| M::Receipt::from_rejection(cloned.as_ref(), reason)) } #[cfg(all(target_os = "linux", feature = "std", feature = "cpu_ram_metrics"))] @@ -82,54 +85,9 @@ impl<'h, M: KernelCallbackObject, S: SubstateDatabase> BootLoader<'h, M, S> { } } - /// Checks that references exist in the store - fn check_references( - &mut self, - callback: &mut M, - references: &IndexSet, - ) -> Result<(IndexSet, IndexSet), BootloadingError> { - let mut global_addresses = indexset!(); - let mut direct_accesses = indexset!(); - - for reference in references.iter() { - let node_id = &reference.0; - - if ALWAYS_VISIBLE_GLOBAL_NODES.contains(node_id) { - // Allow always visible node and do not add reference - continue; - } - - if node_id.is_global_virtual() { - // Allow global virtual and add reference - global_addresses.insert(GlobalAddress::new_or_panic(node_id.clone().into())); - continue; - } - - let ref_value = self - .track - .read_substate( - node_id, - TYPE_INFO_FIELD_PARTITION, - &TypeInfoField::TypeInfo.into(), - ) - .ok_or_else(|| BootloadingError::ReferencedNodeDoesNotExist(*node_id))?; - - match callback.verify_boot_ref_value(node_id, ref_value)? { - StableReferenceType::Global => { - global_addresses.insert(GlobalAddress::new_or_panic(node_id.clone().into())); - } - StableReferenceType::DirectAccess => { - direct_accesses.insert(InternalAddress::new_or_panic(node_id.clone().into())); - } - } - } - - Ok((global_addresses, direct_accesses)) - } - - fn execute_internal<'a>( + fn execute_internal( mut self, - executable: &Executable, + executable: Rc, ) -> Result { #[cfg(feature = "resource_tracker")] radix_engine_profiling::QEMU_PLUGIN_CALIBRATOR.with(|v| { @@ -149,37 +107,31 @@ impl<'h, M: KernelCallbackObject, S: SubstateDatabase> BootLoader<'h, M, S> { .unwrap_or(KernelBoot::babylon()); // Create System - let mut callback = M::init(&mut self.track, executable, self.init.clone())?; + let threads = executable.threads(); + let (mut callback, init_call_frames) = M::init(&mut self.track, executable, self.init.clone())?; // Kernel Initialization let mut kernel = { - // Check references - let (global_addresses, internal_addresses) = self - .check_references(&mut callback, executable.references()) - .map_err(RejectionReason::BootloadingError)?; - Kernel::new( &mut self.track, &mut self.id_allocator, &mut callback, - global_addresses, - internal_addresses, + init_call_frames, ) }; // Execution let result = || -> Result { // Invoke transaction processor - let output = M::start( - &mut kernel, - executable.encoded_instructions(), - executable.pre_allocated_addresses(), - executable.references(), - executable.blobs(), - )?; + let output = M::start(&mut kernel)?; // Sanity check call frame - assert!(kernel.prev_frame_stack.is_empty()); + assert!(kernel + .threads + .get(kernel.cur_thread) + .unwrap() + .prev_frames + .is_empty()); // Sanity check heap assert!(kernel.substate_io.heap.is_empty()); @@ -193,12 +145,26 @@ impl<'h, M: KernelCallbackObject, S: SubstateDatabase> BootLoader<'h, M, S> { .map_err(|e| TransactionExecutionError::RuntimeError(e)); // Create receipt representing the result of a transaction - let receipt = M::create_receipt(callback, self.track, executable, result); + let receipt = M::create_receipt(callback, self.track, result); Ok(receipt) } } +pub struct KernelStack { + current_frame: CallFrame, + prev_frames: Vec>, +} + +impl KernelStack { + fn new(root: CallFrame) -> Self { + Self { + current_frame: root, + prev_frames: vec![], + } + } +} + pub struct Kernel< 'g, // Lifetime of values outliving all frames M, // Upstream System layer @@ -207,12 +173,8 @@ pub struct Kernel< M: KernelCallbackObject, S: CommitableSubstateStore, { - /// Stack - current_frame: CallFrame, - // This stack could potentially be removed and just use the native stack - // but keeping this call_frames stack may potentially prove useful if implementing - // execution pause and/or for better debuggability - prev_frame_stack: Vec>, + cur_thread: usize, + threads: Vec>, substate_io: SubstateIO<'g, S>, @@ -229,34 +191,22 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore + BootStore> Kernel id_allocator: &'g mut IdAllocator, callback: &'g mut M, ) -> Self { - Self::new( - store, - id_allocator, - callback, - index_set_new(), - index_set_new(), - ) + Self::new(store, id_allocator, callback, vec![Default::default()]) } pub fn new( store: &'g mut S, id_allocator: &'g mut IdAllocator, callback: &'g mut M, - global_addresses: IndexSet, - internal_addresses: IndexSet, + init_call_frames: Vec, ) -> Self { - let call_frame = { - let mut call_frame = CallFrame::new_root(M::CallFrameData::root()); - // Add visibility - for global_ref in global_addresses { - call_frame.add_global_reference(global_ref); - } - for direct_access in internal_addresses { - call_frame.add_direct_access_reference(direct_access); - } - - call_frame - }; + let threads = init_call_frames + .into_iter() + .map(|init_refs| { + let call_frame = CallFrame::new_root(M::CallFrameData::root(), init_refs); + KernelStack::new(call_frame) + }) + .collect(); Kernel { substate_io: SubstateIO { @@ -268,8 +218,8 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore + BootStore> Kernel pinned_to_heap: BTreeSet::new(), }, id_allocator, - current_frame: call_frame, - prev_frame_stack: vec![], + cur_thread: 0, + threads, callback, } } @@ -280,6 +230,7 @@ struct KernelHandler< M: KernelCallbackObject, F: FnMut(&mut KernelReadOnly, IOAccess) -> Result<(), RuntimeError>, > { + current_thread: usize, callback: &'a mut M, prev_frame: Option<&'a CallFrame>, on_io_access: F, @@ -298,6 +249,7 @@ impl< io_access: IOAccess, ) -> Result<(), RuntimeError> { let mut read_only = KernelReadOnly { + current_thread: self.current_thread, current_frame, prev_frame: self.prev_frame, heap, @@ -323,6 +275,7 @@ impl< device: SubstateDevice, ) -> Result<(), Self::Error> { let mut read_only = KernelReadOnly { + current_thread: self.current_thread, current_frame, prev_frame: self.prev_frame, heap, @@ -342,15 +295,54 @@ impl< macro_rules! as_read_only { ($kernel:expr) => {{ + let thread = $kernel.threads.get($kernel.cur_thread).unwrap(); KernelReadOnly { - current_frame: &$kernel.current_frame, - prev_frame: $kernel.prev_frame_stack.last(), + current_thread: $kernel.cur_thread, + current_frame: &thread.current_frame, + prev_frame: thread.prev_frames.last(), heap: &$kernel.substate_io.heap, callback: $kernel.callback, } }}; } +impl<'g, M, S> KernelThreadApi for Kernel<'g, M, S> + where + M: KernelCallbackObject, + S: CommitableSubstateStore, +{ + fn kernel_send(&mut self, to_thread: usize, value: IndexedScryptoValue) -> Result<(), RuntimeError> { + let message = CallFrameMessage::from_output(&value); + + let mut mut_threads: Vec<_> = self.threads.iter_mut().map(|x| Some(x)).collect(); + + let cur_thread = mut_threads[self.cur_thread].take().unwrap(); + let to_frame = mut_threads[to_thread].take().unwrap(); + + CallFrame::pass_message( + &self.substate_io, + &mut cur_thread.current_frame, + &mut to_frame.current_frame, + message, + ) + .map_err(CallFrameError::PassMessageError) + .map_err(KernelError::CallFrameError)?; + + Ok(()) + } + + fn kernel_switch_context(&mut self, thread: usize) -> Result<(), RuntimeError> { + self.cur_thread = thread; + Ok(()) + } + + fn kernel_update_call_frame_data(&mut self, update: M::CallFrameData) -> Result<(), RuntimeError> { + let data = self.threads.get_mut(self.cur_thread).unwrap().current_frame.data_mut(); + *data = update; + Ok(()) + } +} + impl<'g, M, S> KernelNodeApi for Kernel<'g, M, S> where M: KernelCallbackObject, @@ -360,7 +352,10 @@ where fn kernel_pin_node(&mut self, node_id: NodeId) -> Result<(), RuntimeError> { self.callback.on_pin_node(&node_id)?; - self.current_frame + self.threads + .get_mut(self.cur_thread) + .unwrap() + .current_frame .pin_node(&mut self.substate_io, node_id) .map_err(|e| { RuntimeError::KernelError(KernelError::CallFrameError( @@ -388,15 +383,19 @@ where CreateNodeEvent::Start(&node_id, &node_substates), )?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_create_node(api, CreateNodeEvent::IOAccess(&io_access)) }, }; - self.current_frame + thread + .current_frame .create_node(&mut self.substate_io, &mut handler, node_id, node_substates) .map_err(|e| match e { CallbackError::Error(e) => RuntimeError::KernelError(KernelError::CallFrameError( @@ -425,15 +424,19 @@ where CreateNodeEvent::Start(&node_id, &node_substates), )?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_create_node(api, CreateNodeEvent::IOAccess(&io_access)) }, }; - self.current_frame + thread + .current_frame .create_node( &mut self.substate_io, &mut handler, @@ -452,16 +455,20 @@ where } { + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_move_module(api, MoveModuleEvent::IOAccess(&io_access)) }, }; for (dest_partition_number, (src_node_id, src_partition_number)) in partitions { - self.current_frame + thread + .current_frame .move_partition( &mut self.substate_io, &mut handler, @@ -489,14 +496,17 @@ where M::on_drop_node_mut(node_id, self)?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_drop_node(api, DropNodeEvent::IOAccess(&io_access)) }, }; - let dropped_node = self + let dropped_node = thread .current_frame .drop_node(&mut self.substate_io, node_id, &mut handler) .map_err(|e| match e { @@ -523,24 +533,37 @@ where S: CommitableSubstateStore, { fn kernel_get_node_visibility(&self, node_id: &NodeId) -> NodeVisibility { - self.current_frame.get_node_visibility(node_id) + self.threads + .get(self.cur_thread) + .unwrap() + .current_frame + .get_node_visibility(node_id) } fn kernel_get_current_depth(&self) -> usize { - self.current_frame.depth() + self.threads + .get(self.cur_thread) + .unwrap() + .current_frame + .depth() + } + + fn kernel_get_current_thread(&self) -> usize { + self.cur_thread } fn kernel_get_system_state(&mut self) -> SystemState<'_, M> { - let caller_actor = match self.prev_frame_stack.last() { + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let caller_actor = match thread.prev_frames.last() { Some(call_frame) => call_frame.data(), None => { // This will only occur on initialization - self.current_frame.data() + thread.current_frame.data() } }; SystemState { system: &mut self.callback, - current_call_frame: self.current_frame.data(), + current_call_frame: thread.current_frame.data(), caller_call_frame: caller_actor, } } @@ -560,6 +583,7 @@ struct KernelReadOnly<'g, M> where M: KernelCallbackObject, { + current_thread: usize, current_frame: &'g CallFrame, prev_frame: Option<&'g CallFrame>, heap: &'g Heap, @@ -578,6 +602,10 @@ where self.current_frame.depth() } + fn kernel_get_current_thread(&self) -> usize { + self.current_thread + } + fn kernel_get_system_state(&mut self) -> SystemState<'_, M> { let caller_call_frame = match self.prev_frame { Some(call_frame) => call_frame.data(), @@ -757,7 +785,10 @@ where self.callback .on_mark_substate_as_transient(&node_id, &partition_num, &key)?; - self.current_frame + self.threads + .get_mut(self.cur_thread) + .unwrap() + .current_frame .mark_substate_as_transient(&mut self.substate_io, node_id, partition_num, key) .map_err(|e| { RuntimeError::KernelError(KernelError::CallFrameError( @@ -787,15 +818,18 @@ where }, )?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_open_substate(api, OpenSubstateEvent::IOAccess(&io_access)) }, }; - let maybe_lock_handle = self.current_frame.open_substate( + let maybe_lock_handle = thread.current_frame.open_substate( &mut self.substate_io, node_id, partition_num, @@ -814,15 +848,19 @@ where M::on_substate_lock_fault(*node_id, partition_num, &substate_key, self)?; if retry { + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_open_substate(api, OpenSubstateEvent::IOAccess(&io_access)) }, }; - self.current_frame + thread + .current_frame .open_substate( &mut self.substate_io, &node_id, @@ -879,7 +917,10 @@ where &mut self, lock_handle: SubstateHandle, ) -> Result { - self.current_frame + self.threads + .get_mut(self.cur_thread) + .unwrap() + .current_frame .get_handle_info(lock_handle) .ok_or(RuntimeError::KernelError( KernelError::SubstateHandleDoesNotExist(lock_handle), @@ -891,15 +932,18 @@ where &mut self, lock_handle: SubstateHandle, ) -> Result<&IndexedScryptoValue, RuntimeError> { + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_read_substate(api, ReadSubstateEvent::IOAccess(&io_access)) }, }; - let value = self + let value = thread .current_frame .read_substate(&mut self.substate_io, lock_handle, &mut handler) .map_err(|e| match e { @@ -927,15 +971,19 @@ where }, )?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { M::on_write_substate(api, WriteSubstateEvent::IOAccess(&io_access)) }, }; - self.current_frame + thread + .current_frame .write_substate(&mut self.substate_io, lock_handle, value, &mut handler) .map_err(|e| match e { CallbackError::Error(e) => RuntimeError::KernelError(KernelError::CallFrameError( @@ -956,7 +1004,10 @@ where let mut read_only = as_read_only!(self); M::on_close_substate(&mut read_only, CloseSubstateEvent::Start(lock_handle))?; - self.current_frame + self.threads + .get_mut(self.cur_thread) + .unwrap() + .current_frame .close_substate(&mut self.substate_io, lock_handle) .map_err(|e| { RuntimeError::KernelError(KernelError::CallFrameError( @@ -982,16 +1033,20 @@ where &value, ))?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { api.callback .on_set_substate(SetSubstateEvent::IOAccess(&io_access)) }, }; - self.current_frame + thread + .current_frame .set_substate( &mut self.substate_io, node_id, @@ -1024,16 +1079,19 @@ where substate_key, ))?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { api.callback .on_remove_substate(RemoveSubstateEvent::IOAccess(&io_access)) }, }; - let substate = self + let substate = thread .current_frame .remove_substate( &mut self.substate_io, @@ -1061,16 +1119,19 @@ where ) -> Result, RuntimeError> { self.callback.on_scan_keys(ScanKeysEvent::Start)?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { api.callback .on_scan_keys(ScanKeysEvent::IOAccess(&io_access)) }, }; - let keys = self + let keys = thread .current_frame .scan_keys::( &mut self.substate_io, @@ -1099,16 +1160,19 @@ where self.callback .on_drain_substates(DrainSubstatesEvent::Start(limit))?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { api.callback .on_drain_substates(DrainSubstatesEvent::IOAccess(&io_access)) }, }; - let substates = self + let substates = thread .current_frame .drain_substates::( &mut self.substate_io, @@ -1137,16 +1201,19 @@ where self.callback .on_scan_sorted_substates(ScanSortedSubstatesEvent::Start)?; + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let mut handler = KernelHandler { + current_thread: self.cur_thread, callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame: thread.prev_frames.last(), on_io_access: |api, io_access| { api.callback .on_scan_sorted_substates(ScanSortedSubstatesEvent::IOAccess(&io_access)) }, }; - let substates = self + let substates = thread .current_frame .scan_sorted( &mut self.substate_io, @@ -1187,14 +1254,21 @@ where { let frame = CallFrame::new_child_from_parent( &self.substate_io, - &mut self.current_frame, + &mut self.threads.get_mut(self.cur_thread).unwrap().current_frame, callee, message, ) .map_err(CallFrameError::CreateFrameError) .map_err(KernelError::CallFrameError)?; - let parent = mem::replace(&mut self.current_frame, frame); - self.prev_frame_stack.push(parent); + let parent = mem::replace( + &mut self.threads.get_mut(self.cur_thread).unwrap().current_frame, + frame, + ); + self.threads + .get_mut(self.cur_thread) + .unwrap() + .prev_frames + .push(parent); } // Execute @@ -1203,10 +1277,19 @@ where M::on_execution_start(self)?; // Auto drop locks - for handle in self.current_frame.open_substates() { + for handle in self + .threads + .get(self.cur_thread) + .unwrap() + .current_frame + .open_substates() + { M::on_close_substate(self, CloseSubstateEvent::Start(handle))?; } - self.current_frame + self.threads + .get_mut(self.cur_thread) + .unwrap() + .current_frame .close_all_substates(&mut self.substate_io); // Run @@ -1214,10 +1297,19 @@ where let message = CallFrameMessage::from_output(&output); // Auto-drop locks again in case module forgot to drop - for handle in self.current_frame.open_substates() { + for handle in self + .threads + .get(self.cur_thread) + .unwrap() + .current_frame + .open_substates() + { M::on_close_substate(self, CloseSubstateEvent::Start(handle))?; } - self.current_frame + self.threads + .get_mut(self.cur_thread) + .unwrap() + .current_frame .close_all_substates(&mut self.substate_io); // Handle execution finish @@ -1228,12 +1320,13 @@ where // Move { - let parent = self.prev_frame_stack.last_mut().unwrap(); + let thread = self.threads.get_mut(self.cur_thread).unwrap(); + let parent = thread.prev_frames.last_mut().unwrap(); // Move resource CallFrame::pass_message( &self.substate_io, - &mut self.current_frame, + &mut thread.current_frame, parent, message.clone(), ) @@ -1241,11 +1334,21 @@ where .map_err(KernelError::CallFrameError)?; // Auto-drop - let owned_nodes = self.current_frame.owned_nodes(); + let owned_nodes = self + .threads + .get(self.cur_thread) + .unwrap() + .current_frame + .owned_nodes(); M::auto_drop(owned_nodes, self)?; // Now, check if any own has been left! - let owned_nodes = self.current_frame.owned_nodes(); + let owned_nodes = self + .threads + .get(self.cur_thread) + .unwrap() + .current_frame + .owned_nodes(); if !owned_nodes.is_empty() { return Err(RuntimeError::KernelError(KernelError::OrphanedNodes( owned_nodes, @@ -1255,8 +1358,17 @@ where // Pop call frame { - let parent = self.prev_frame_stack.pop().unwrap(); - let _ = core::mem::replace(&mut self.current_frame, parent); + let parent = self + .threads + .get_mut(self.cur_thread) + .unwrap() + .prev_frames + .pop() + .unwrap(); + let _ = core::mem::replace( + &mut self.threads.get_mut(self.cur_thread).unwrap().current_frame, + parent, + ); } M::after_invoke(&output, self)?; @@ -1282,12 +1394,15 @@ where substate_io: SubstateIO<'g, S>, id_allocator: &'g mut IdAllocator, current_frame: CallFrame, - prev_frame_stack: Vec>, + prev_frames: Vec>, callback: &'g mut M, ) -> Kernel<'g, M, S> { Self { - current_frame, - prev_frame_stack, + cur_thread: 0usize, + threads: vec![KernelStack { + current_frame, + prev_frames, + }], substate_io, id_allocator, callback, @@ -1298,7 +1413,7 @@ where &self, ) -> &CallFrame<::CallFrameData, ::LockData> { - &self.current_frame + &self.threads.get(self.cur_thread).unwrap().current_frame } pub fn kernel_current_frame_mut( @@ -1310,7 +1425,10 @@ where ::LockData, >, ) { - (&self.substate_io, &mut self.current_frame) + ( + &self.substate_io, + &mut self.threads.get_mut(self.cur_thread).unwrap().current_frame, + ) } pub fn kernel_prev_frame_stack( @@ -1321,7 +1439,7 @@ where ::LockData, >, > { - &self.prev_frame_stack + &self.threads.get(self.cur_thread).unwrap().prev_frames } pub fn kernel_prev_frame_stack_mut( @@ -1332,7 +1450,7 @@ where ::LockData, >, > { - &mut self.prev_frame_stack + &mut self.threads.get_mut(self.cur_thread).unwrap().prev_frames } pub fn kernel_substate_io(&self) -> &SubstateIO<'g, S> { diff --git a/radix-engine/src/kernel/kernel_api.rs b/radix-engine/src/kernel/kernel_api.rs index a6fb27959cb..b71235234de 100644 --- a/radix-engine/src/kernel/kernel_api.rs +++ b/radix-engine/src/kernel/kernel_api.rs @@ -15,6 +15,15 @@ pub struct DroppedNode { // Following the convention of Linux Kernel API, https://www.kernel.org/doc/htmldocs/kernel-api/, // all methods are prefixed by the subsystem of kernel. + +pub trait KernelThreadApi { + fn kernel_send(&mut self, thread: usize, value: IndexedScryptoValue) -> Result<(), RuntimeError>; + + fn kernel_switch_context(&mut self, thread: usize) -> Result<(), RuntimeError>; + + fn kernel_update_call_frame_data(&mut self, update: M::CallFrameData) -> Result<(), RuntimeError>; +} + /// API for managing nodes pub trait KernelNodeApi { /// Pin a node to it's current device. @@ -191,6 +200,8 @@ pub trait KernelInternalApi { /// Gets the number of call frames that are currently in the call frame stack fn kernel_get_current_depth(&self) -> usize; + fn kernel_get_current_thread(&self) -> usize; + /// Returns the visibility of a node fn kernel_get_node_visibility(&self, node_id: &NodeId) -> NodeVisibility; @@ -200,7 +211,8 @@ pub trait KernelInternalApi { } pub trait KernelApi: - KernelNodeApi + KernelThreadApi + + KernelNodeApi + KernelSubstateApi + KernelInvokeApi + KernelInternalApi diff --git a/radix-engine/src/kernel/kernel_callback_api.rs b/radix-engine/src/kernel/kernel_callback_api.rs index 810264b51e3..4083279e6d8 100644 --- a/radix-engine/src/kernel/kernel_callback_api.rs +++ b/radix-engine/src/kernel/kernel_callback_api.rs @@ -1,4 +1,4 @@ -use super::call_frame::{CallFrameMessage, StableReferenceType}; +use super::call_frame::{CallFrameMessage, RootCallFrameInitRefs, StableReferenceType}; use crate::errors::*; use crate::internal_prelude::*; use crate::kernel::kernel_api::KernelInvocation; @@ -11,7 +11,7 @@ use radix_engine_interface::api::field_api::LockFlags; use radix_substate_store_interface::db_key_mapper::SpreadPrefixKeyMapper; use radix_substate_store_interface::interface::SubstateDatabase; use radix_transactions::model::Executable; -use radix_transactions::prelude::PreAllocatedAddress; +use radix_transactions::prelude::ExecutableThread; pub trait CallFrameReferences { fn root() -> Self; @@ -159,25 +159,13 @@ pub trait KernelCallbackObject: Sized { /// Create the callback object (system layer) with data loaded from the substate store fn init( store: &mut S, - executable: &Executable, + executable: Rc, init: Self::Init, - ) -> Result; + ) -> Result<(Self, Vec), RejectionReason>; - /// Verifies and returns the type of a given reference used during boot - fn verify_boot_ref_value( - &mut self, - node_id: &NodeId, - value: &IndexedScryptoValue, - ) -> Result; + /// Start transaction execution + fn start>(api: &mut Y) -> Result; - /// Start execution - fn start>( - api: &mut Y, - manifest_encoded_instructions: &[u8], - pre_allocated_addresses: &Vec, - references: &IndexSet, - blobs: &IndexMap>, - ) -> Result; /// Finish execution fn finish(&mut self, store_commit_info: StoreCommitInfo) -> Result<(), RuntimeError>; @@ -186,7 +174,6 @@ pub trait KernelCallbackObject: Sized { fn create_receipt( self, track: Track, - executable: &Executable, result: Result, ) -> Self::Receipt; diff --git a/radix-engine/src/system/bootstrap.rs b/radix-engine/src/system/bootstrap.rs index eb9937ddf83..a8b52ea2391 100644 --- a/radix-engine/src/system/bootstrap.rs +++ b/radix-engine/src/system/bootstrap.rs @@ -412,10 +412,10 @@ where self.vm_init.clone(), &ExecutionConfig::for_genesis_transaction(self.network_definition.clone()) .with_kernel_trace(self.trace), - &transaction + Rc::new(transaction .prepare() .expect("Expected system bootstrap transaction to be preparable") - .get_executable(btreeset![system_execution(SystemExecution::Protocol)]), + .get_executable(btreeset![system_execution(SystemExecution::Protocol)])), ); let commit_result = receipt.expect_commit(true); @@ -441,10 +441,10 @@ where self.vm_init.clone(), &ExecutionConfig::for_genesis_transaction(self.network_definition.clone()) .with_kernel_trace(self.trace), - &transaction + Rc::new(transaction .prepare() .expect("Expected genesis data chunk transaction to be preparable") - .get_executable(btreeset![system_execution(SystemExecution::Protocol)]), + .get_executable(btreeset![system_execution(SystemExecution::Protocol)])), ); let commit_result = receipt.expect_commit(true); @@ -465,10 +465,10 @@ where self.vm_init.clone(), &ExecutionConfig::for_genesis_transaction(self.network_definition.clone()) .with_kernel_trace(self.trace), - &transaction + Rc::new(transaction .prepare() .expect("Expected genesis wrap up transaction to be preparable") - .get_executable(btreeset![system_execution(SystemExecution::Protocol)]), + .get_executable(btreeset![system_execution(SystemExecution::Protocol)])), ); let commit_result = receipt.expect_commit(true); diff --git a/radix-engine/src/system/system.rs b/radix-engine/src/system/system.rs index b94f46206d5..4a97ad90524 100644 --- a/radix-engine/src/system/system.rs +++ b/radix-engine/src/system/system.rs @@ -29,6 +29,7 @@ use crate::track::interface::NodeSubstates; use radix_blueprint_schema_init::{Condition, KeyValueStoreGenericSubstitutions}; #[cfg(not(feature = "alloc"))] use radix_common_derive::*; +use radix_common_derive::catch_unwind; use radix_engine_interface::api::actor_api::EventFlags; use radix_engine_interface::api::actor_index_api::SystemActorIndexApi; use radix_engine_interface::api::field_api::{FieldHandle, LockFlags}; @@ -40,12 +41,15 @@ use radix_engine_interface::api::key_value_store_api::{ }; use radix_engine_interface::api::object_api::ModuleId; use radix_engine_interface::api::*; +use radix_engine_interface::api::thread_api::SystemThreadApi; use radix_engine_interface::blueprints::package::*; use radix_engine_interface::blueprints::resource::*; +use radix_engine_interface::blueprints::transaction_processor::TRANSACTION_PROCESSOR_BLUEPRINT; use radix_engine_profiling_derive::trace_resources; use radix_substate_store_interface::db_key_mapper::SubstateKeyContent; use sbor::rust::string::ToString; use sbor::rust::vec::Vec; +use crate::system::system_modules::auth::AuthModule; pub const BOOT_LOADER_SYSTEM_VERSION_FIELD_KEY: FieldKey = 1u8; @@ -2755,6 +2759,71 @@ impl<'a, Y: KernelApi>, V: SystemCallbackObject> SystemExecutionTraceA } } +#[cfg_attr( + feature = "std", + catch_unwind(crate::utils::catch_unwind_system_panic_transformer) +)] +impl<'a, Y: KernelApi>, V: SystemCallbackObject> SystemThreadApi +for SystemService<'a, Y, V> +{ + fn move_to_stack(&mut self, thread: usize, value: IndexedScryptoValue) -> Result<(), RuntimeError> { + self.api.kernel_send(thread, value) + } + + fn switch_stack(&mut self, thread: usize) -> Result<(), RuntimeError> { + //let auth_zone = SystemModuleMixer::on_call_function(self, &blueprint_id, function_name)?; + + self.api.kernel_switch_context(thread)?; + + let cur_frame = self.api.kernel_get_system_state().current_call_frame; + match cur_frame { + Actor::Root => { + let (virtual_resources, virtual_non_fungibles) = { + let auth_module = &self.api.kernel_get_system().modules.auth; + let virtual_resources = + auth_module.params.thread_params[thread] + .virtual_resources + .clone(); + let virtual_non_fungibles = + auth_module.params.thread_params[thread] + .initial_proofs + .clone(); + + (virtual_resources, virtual_non_fungibles) + }; + + let auth_zone = AuthModule::create_auth_zone(self, None, virtual_resources, virtual_non_fungibles)?; + + self.api.kernel_update_call_frame_data(Actor::Function( + FunctionActor { + blueprint_id: BlueprintId::new(&TRANSACTION_PROCESSOR_PACKAGE, TRANSACTION_PROCESSOR_BLUEPRINT), + ident: "run".to_string(), + auth_zone, + } + ))?; + } + _ => { + } + } + + Ok(()) + } + + + fn free_stack(&mut self, thread: usize) -> Result<(), RuntimeError> { + let cur_frame = self.api.kernel_get_system_state().current_call_frame; + match cur_frame { + Actor::Function(FunctionActor { auth_zone, .. }) => { + let auth_zone = auth_zone.clone(); + self.api.kernel_drop_node(&auth_zone)?; + } + _ => {} + } + + Ok(()) + } +} + #[cfg_attr( feature = "std", catch_unwind(crate::utils::catch_unwind_system_panic_transformer) @@ -3018,6 +3087,10 @@ impl<'a, Y: KernelApi>, V: SystemCallbackObject> KernelInternalApi usize { + self.api.kernel_get_current_thread() + } + fn kernel_get_node_visibility(&self, node_id: &NodeId) -> NodeVisibility { self.api.kernel_get_node_visibility(node_id) } diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index 39540b0c3a8..1edade3c3c2 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -13,16 +13,16 @@ use crate::blueprints::resource::{ BurnFungibleResourceEvent, FungibleVaultBalanceFieldPayload, FungibleVaultBalanceFieldSubstate, FungibleVaultField, }; -use crate::blueprints::transaction_processor::TransactionProcessorRunInputEfficientEncodable; +use crate::blueprints::transaction_processor::{TransactionProcessorRunInputEfficientEncodable, TransactionProcessorThreadRunInputEfficientEncodable}; use crate::blueprints::transaction_tracker::{ TransactionStatus, TransactionStatusV1, TransactionTrackerSubstate, }; use crate::errors::*; use crate::internal_prelude::*; -use crate::kernel::call_frame::{CallFrameMessage, StableReferenceType}; +use crate::kernel::call_frame::{CallFrameMessage, RootCallFrameInitRefs, StableReferenceType}; use crate::kernel::kernel_api::{KernelApi, KernelInvocation}; use crate::kernel::kernel_api::{KernelInternalApi, KernelSubstateApi}; -use crate::kernel::kernel_callback_api::RefCheckEvent; +use crate::kernel::kernel_callback_api::{RefCheckEvent}; use crate::kernel::kernel_callback_api::{ CloseSubstateEvent, CreateNodeEvent, DrainSubstatesEvent, DropNodeEvent, KernelCallbackObject, MoveModuleEvent, OpenSubstateEvent, ReadSubstateEvent, RemoveSubstateEvent, ScanKeysEvent, @@ -34,7 +34,7 @@ use crate::system::actor::FunctionActor; use crate::system::actor::MethodActor; use crate::system::module::{InitSystemModule, SystemModule}; use crate::system::system::SystemService; -use crate::system::system_callback_api::SystemCallbackObject; +use crate::system::system_callback_api::{SystemCallbackObject}; use crate::system::system_db_reader::SystemDatabaseReader; use crate::system::system_modules::auth::AuthModule; use crate::system::system_modules::costing::{ @@ -70,7 +70,9 @@ use radix_engine_interface::blueprints::transaction_processor::{ InstructionOutput, TRANSACTION_PROCESSOR_BLUEPRINT, TRANSACTION_PROCESSOR_RUN_IDENT, }; use radix_substate_store_interface::{db_key_mapper::SpreadPrefixKeyMapper, interface::*}; -use radix_transactions::model::{Executable, PreAllocatedAddress, TransactionIntentHash}; +use radix_transactions::model::{ + Executable, ExecutableThread, PreAllocatedAddress, TransactionIntentHash, +}; pub const BOOT_LOADER_SYSTEM_SUBSTATE_FIELD_KEY: FieldKey = 1u8; @@ -158,6 +160,7 @@ pub struct System { pub schema_cache: NonIterMap>, pub auth_cache: NonIterMap, pub modules: SystemModuleMixer, + pub executable: Rc, } impl System { @@ -170,12 +173,7 @@ impl System { "Transaction costing parameters: {:?}", executable.costing_parameters() ); - println!( - "Pre-allocated addresses: {:?}", - executable.pre_allocated_addresses() - ); - println!("Blobs: {:?}", executable.blobs().keys()); - println!("References: {:?}", executable.references()); + println!("Threads: {:?}", executable.threads()); } fn read_epoch(store: &mut S) -> Option { @@ -768,6 +766,118 @@ impl System { | TypeInfoSubstate::GlobalAddressPhantom(_) => Ok(()), } } + + + /// Checks that references exist in the store + fn check_references( + &mut self, + store: &mut S, + ) -> Result, BootloadingError> { + let mut initial_call_frames = vec![]; + + for thread in self.executable.threads() { + let mut global_addresses = indexset!(); + let mut direct_accesses = indexset!(); + + for reference in thread.references.iter() { + let node_id = &reference.0; + + if ALWAYS_VISIBLE_GLOBAL_NODES.contains(node_id) { + // Allow always visible node and do not add reference + continue; + } + + if node_id.is_global_virtual() { + // Allow global virtual and add reference + global_addresses.insert(GlobalAddress::new_or_panic(node_id.clone().into())); + continue; + } + + let ref_value = store + .read_substate( + node_id, + TYPE_INFO_FIELD_PARTITION, + &TypeInfoField::TypeInfo.into(), + ) + .ok_or_else(|| BootloadingError::ReferencedNodeDoesNotExist(*node_id))?; + + match Self::verify_boot_ref_value(&mut self.modules, node_id, ref_value)? { + StableReferenceType::Global => { + global_addresses + .insert(GlobalAddress::new_or_panic(node_id.clone().into())); + } + StableReferenceType::DirectAccess => { + direct_accesses + .insert(InternalAddress::new_or_panic(node_id.clone().into())); + } + } + } + + let init_refs = RootCallFrameInitRefs { + global_addresses, + direct_accesses, + }; + initial_call_frames.push(init_refs); + } + + // HACK + let (first, rest) = initial_call_frames.split_first_mut().unwrap(); + for r in rest { + first.direct_accesses.extend(r.direct_accesses.clone()); + first.global_addresses.extend(r.global_addresses.clone()); + } + + Ok(initial_call_frames) + } + + fn verify_boot_ref_value( + modules: &mut SystemModuleMixer, + node_id: &NodeId, + ref_value: &IndexedScryptoValue, + ) -> Result { + if let Some(costing) = modules.costing_mut() { + let io_access = IOAccess::ReadFromDb( + CanonicalSubstateKey { + node_id: *node_id, + partition_number: TYPE_INFO_FIELD_PARTITION, + substate_key: SubstateKey::Field(TypeInfoField::TypeInfo.field_index()), + }, + ref_value.len(), + ); + let event = RefCheckEvent::IOAccess(&io_access); + + costing + .apply_deferred_execution_cost(ExecutionCostingEntry::RefCheck { event: &event }) + .map_err(|e| BootloadingError::FailedToApplyDeferredCosts(e))?; + } + + let type_substate: TypeInfoSubstate = ref_value.as_typed().unwrap(); + return match &type_substate { + TypeInfoSubstate::Object( + info @ ObjectInfo { + blueprint_info: BlueprintInfo { blueprint_id, .. }, + .. + }, + ) => { + if info.is_global() { + Ok(StableReferenceType::Global) + } else if blueprint_id.package_address.eq(&RESOURCE_PACKAGE) + && (blueprint_id.blueprint_name.eq(FUNGIBLE_VAULT_BLUEPRINT) + || blueprint_id.blueprint_name.eq(NON_FUNGIBLE_VAULT_BLUEPRINT)) + { + Ok(StableReferenceType::DirectAccess) + } else { + Err(BootloadingError::ReferencedNodeDoesNotAllowDirectAccess( + node_id.clone(), + )) + } + } + _ => Err(BootloadingError::ReferencedNodeIsNotAnObject( + node_id.clone(), + )), + }; + } + } impl KernelCallbackObject for System { @@ -779,9 +889,9 @@ impl KernelCallbackObject for System { fn init( store: &mut S, - executable: &Executable, + executable: Rc, init_input: SystemInit, - ) -> Result { + ) -> Result<(Self, Vec), RejectionReason> { // Dump executable #[cfg(not(feature = "alloc"))] if init_input.enable_kernel_trace { @@ -919,96 +1029,75 @@ impl KernelCallbackObject for System { } } - let system = System { + let mut system = System { + executable, blueprint_cache: NonIterMap::new(), auth_cache: NonIterMap::new(), schema_cache: NonIterMap::new(), callback, modules, }; - Ok(system) - } - fn verify_boot_ref_value( - &mut self, - node_id: &NodeId, - ref_value: &IndexedScryptoValue, - ) -> Result { - if let Some(costing) = self.modules.costing_mut() { - let io_access = IOAccess::ReadFromDb( - CanonicalSubstateKey { - node_id: *node_id, - partition_number: TYPE_INFO_FIELD_PARTITION, - substate_key: SubstateKey::Field(TypeInfoField::TypeInfo.field_index()), - }, - ref_value.len(), - ); - let event = RefCheckEvent::IOAccess(&io_access); + let call_frame_init = system.check_references(store).map_err(RejectionReason::BootloadingError)?; - costing - .apply_deferred_execution_cost(ExecutionCostingEntry::RefCheck { event: &event }) - .map_err(|e| BootloadingError::FailedToApplyDeferredCosts(e))?; - } - - let type_substate: TypeInfoSubstate = ref_value.as_typed().unwrap(); - return match &type_substate { - TypeInfoSubstate::Object( - info @ ObjectInfo { - blueprint_info: BlueprintInfo { blueprint_id, .. }, - .. - }, - ) => { - if info.is_global() { - Ok(StableReferenceType::Global) - } else if blueprint_id.package_address.eq(&RESOURCE_PACKAGE) - && (blueprint_id.blueprint_name.eq(FUNGIBLE_VAULT_BLUEPRINT) - || blueprint_id.blueprint_name.eq(NON_FUNGIBLE_VAULT_BLUEPRINT)) - { - Ok(StableReferenceType::DirectAccess) - } else { - Err(BootloadingError::ReferencedNodeDoesNotAllowDirectAccess( - node_id.clone(), - )) - } - } - _ => Err(BootloadingError::ReferencedNodeIsNotAnObject( - node_id.clone(), - )), - }; + Ok((system, call_frame_init)) } + fn start>( api: &mut Y, - manifest_encoded_instructions: &[u8], - pre_allocated_addresses: &Vec, - references: &IndexSet, - blobs: &IndexMap>, ) -> Result, RuntimeError> { + let threads = api.kernel_get_system_state().system.executable.threads(); + let init: Vec<_> = threads.iter().map(|thread| { + let manifest_encoded_instructions = thread.encoded_instructions.clone(); + let references = thread.references.clone(); + let blobs = thread.blobs.clone(); + let pre_allocated_addresses = thread.pre_allocated_addresses.clone(); + (thread.id, manifest_encoded_instructions, references, blobs, pre_allocated_addresses) + }).collect(); + let mut system = SystemService::new(api); - // Allocate global addresses - let mut global_address_reservations = Vec::new(); - for PreAllocatedAddress { - blueprint_id, - address, - } in pre_allocated_addresses - { - let global_address_reservation = - system.prepare_global_address(blueprint_id.clone(), address.clone())?; - global_address_reservations.push(global_address_reservation); + let mut threads = vec![]; + for (id, manifest_encoded_instructions, references, blobs, pre_allocated_addresses) in init { + // Allocate global addresses + let mut global_address_reservations = Vec::new(); + for PreAllocatedAddress { + blueprint_id, + address, + } in &pre_allocated_addresses + { + let global_address_reservation = + system.prepare_global_address(blueprint_id.clone(), address.clone())?; + global_address_reservations.push(global_address_reservation); + } + + threads.push(TransactionProcessorThreadRunInputEfficientEncodable { + id, + manifest_encoded_instructions, + global_address_reservations, + references, + blobs, + }); } + /* + let (first, rest) = threads.split_first_mut().unwrap(); + for other in rest { + first.references.extend(other.references.clone()); + } + */ + + let tx_input = TransactionProcessorRunInputEfficientEncodable { + threads + }; + // Call TX processor let rtn = system.call_function( TRANSACTION_PROCESSOR_PACKAGE, TRANSACTION_PROCESSOR_BLUEPRINT, TRANSACTION_PROCESSOR_RUN_IDENT, - scrypto_encode(&TransactionProcessorRunInputEfficientEncodable { - manifest_encoded_instructions, - global_address_reservations, - references, - blobs, - }) + scrypto_encode(&tx_input) .unwrap(), )?; @@ -1064,7 +1153,6 @@ impl KernelCallbackObject for System { fn create_receipt( self, mut track: Track, - executable: &Executable, interpretation_result: Result, TransactionExecutionError>, ) -> TransactionReceipt { // Panic if an error is encountered in the system layer or below. The following code @@ -1149,7 +1237,7 @@ impl KernelCallbackObject for System { &mut track, costing_module.fee_reserve, is_success, - executable.costing_parameters().free_credit_in_xrd, + self.executable.costing_parameters().free_credit_in_xrd, ); let fee_destination = FeeDestination { to_proposer: fee_reserve_finalization.to_proposer_amount(), @@ -1163,7 +1251,7 @@ impl KernelCallbackObject for System { Self::update_transaction_tracker( &mut track, next_epoch, - executable.intent_hash(), + self.executable.intent_hash(), is_success, ); } @@ -1198,7 +1286,7 @@ impl KernelCallbackObject for System { StateUpdateSummary::new(substate_db, new_node_ids, &state_updates); // Resource reconciliation does not currently work in preview mode - if executable.costing_parameters().free_credit_in_xrd.is_zero() { + if self.executable.costing_parameters().free_credit_in_xrd.is_zero() { let system_reader = SystemDatabaseReader::new_with_overlay(substate_db, &state_updates); reconcile_resource_state_and_events( @@ -1245,7 +1333,7 @@ impl KernelCallbackObject for System { let receipt = TransactionReceipt { costing_parameters, - transaction_costing_parameters: executable.costing_parameters().clone().into(), + transaction_costing_parameters: self.executable.costing_parameters().clone().into(), fee_summary, fee_details, result, @@ -1489,13 +1577,6 @@ impl KernelCallbackObject for System { let output = { C::invoke(&blueprint_id.package_address, export, input, &mut system)? }; - // Validate output - system.validate_blueprint_payload( - &target, - BlueprintPayloadIdentifier::Function(ident.clone(), InputOrOutput::Output), - output.as_vec_ref(), - )?; - Ok(output) } Actor::BlueprintHook(BlueprintHookActor { diff --git a/radix-engine/src/system/system_modules/auth/auth_module.rs b/radix-engine/src/system/system_modules/auth/auth_module.rs index 2bf9e4ec60c..acc0f6de7d0 100644 --- a/radix-engine/src/system/system_modules/auth/auth_module.rs +++ b/radix-engine/src/system/system_modules/auth/auth_module.rs @@ -92,10 +92,15 @@ impl AuthModule { let is_at_root = api.kernel_get_current_depth() == 0; let (virtual_resources, virtual_non_fungibles) = if is_transaction_processor_blueprint && is_at_root { + let thread_id = api.kernel_get_current_thread(); let auth_module = &api.kernel_get_system().modules.auth; ( - auth_module.params.virtual_resources.clone(), - auth_module.params.initial_proofs.clone(), + auth_module.params.thread_params[thread_id] + .virtual_resources + .clone(), + auth_module.params.thread_params[thread_id] + .initial_proofs + .clone(), ) } else { (BTreeSet::new(), BTreeSet::new()) @@ -208,7 +213,7 @@ impl AuthModule { Ok((auth_zone.into_payload().global_caller, Some(handle))) } - fn create_auth_zone>, V: SystemCallbackObject>( + pub(crate) fn create_auth_zone>, V: SystemCallbackObject>( system: &mut SystemService, receiver: Option<(&NodeId, bool)>, virtual_resources: BTreeSet, diff --git a/radix-engine/src/system/system_modules/kernel_trace/module.rs b/radix-engine/src/system/system_modules/kernel_trace/module.rs index 00bc9fcabe8..aecf504137a 100644 --- a/radix-engine/src/system/system_modules/kernel_trace/module.rs +++ b/radix-engine/src/system/system_modules/kernel_trace/module.rs @@ -20,8 +20,8 @@ pub struct KernelTraceModule; #[macro_export] macro_rules! log { ( $api: expr, $msg: expr $( , $arg:expr )* ) => { - #[cfg(not(feature = "alloc"))] - println!("{}[{}] {}", " ".repeat($api.kernel_get_current_depth()), $api.kernel_get_current_depth(), sbor::rust::format!($msg, $( $arg ),*)); + //#[cfg(not(feature = "alloc"))] + //println!("{}[{}] {}", " ".repeat($api.kernel_get_current_depth()), $api.kernel_get_current_depth(), sbor::rust::format!($msg, $( $arg ),*)); }; } diff --git a/radix-engine/src/system/system_modules/module_mixer.rs b/radix-engine/src/system/system_modules/module_mixer.rs index 8939e06038b..5d032dab6e6 100644 --- a/radix-engine/src/system/system_modules/module_mixer.rs +++ b/radix-engine/src/system/system_modules/module_mixer.rs @@ -84,7 +84,7 @@ pub struct SystemModuleMixer { pub(super) kernel_trace: KernelTraceModule, pub(super) limits: LimitsModule, pub(super) costing: CostingModule, - pub(super) auth: AuthModule, + pub(crate) auth: AuthModule, pub(crate) transaction_runtime: TransactionRuntimeModule, pub(super) execution_trace: ExecutionTraceModule, } diff --git a/radix-engine/src/transaction/preview_executor.rs b/radix-engine/src/transaction/preview_executor.rs index 6e63739d58b..e5c8e9511a5 100644 --- a/radix-engine/src/transaction/preview_executor.rs +++ b/radix-engine/src/transaction/preview_executor.rs @@ -1,8 +1,10 @@ +use rust::rc::Rc; use crate::transaction::TransactionReceipt; use crate::transaction::*; use crate::vm::wasm::WasmEngine; use crate::vm::{NativeVmExtension, VmInit}; use radix_common::network::NetworkDefinition; +use radix_rust::rust; use radix_substate_store_interface::interface::*; use radix_transactions::errors::TransactionValidationError; use radix_transactions::model::PreviewIntentV1; @@ -40,6 +42,6 @@ pub fn execute_preview<'s, S: SubstateDatabase, W: WasmEngine, E: NativeVmExtens substate_db, vm_init, &execution_config, - &validated.get_executable(), + Rc::new(validated.get_executable()), )) } diff --git a/radix-engine/src/transaction/transaction_executor.rs b/radix-engine/src/transaction/transaction_executor.rs index 8e244ac1a10..c39529e4177 100644 --- a/radix-engine/src/transaction/transaction_executor.rs +++ b/radix-engine/src/transaction/transaction_executor.rs @@ -305,7 +305,7 @@ where } } - pub fn execute(&mut self, executable: &Executable) -> V::Receipt { + pub fn execute(&mut self, executable: Rc) -> V::Receipt { let kernel_boot = BootLoader { id_allocator: IdAllocator::new(executable.intent_hash().to_hash()), track: Track::<_, SpreadPrefixKeyMapper>::new(self.substate_db), @@ -321,7 +321,7 @@ pub fn execute_transaction_with_configuration, ) -> TransactionReceipt { let mut executor = TransactionExecutor::<_, System>::new( substate_db, @@ -342,7 +342,7 @@ pub fn execute_transaction<'s, S: SubstateDatabase, W: WasmEngine, E: NativeVmEx substate_db: &S, vm_init: VmInit<'s, W, E>, execution_config: &ExecutionConfig, - transaction: &Executable, + transaction: Rc, ) -> TransactionReceipt { execute_transaction_with_configuration::>( substate_db, @@ -361,7 +361,7 @@ pub fn execute_and_commit_transaction< substate_db: &mut S, vms: VmInit<'s, W, E>, execution_config: &ExecutionConfig, - transaction: &Executable, + transaction: Rc, ) -> TransactionReceipt { let receipt = execute_transaction_with_configuration::>( substate_db, diff --git a/radix-engine/src/vm/native_vm.rs b/radix-engine/src/vm/native_vm.rs index bb583e08696..eb9fc680ff8 100644 --- a/radix-engine/src/vm/native_vm.rs +++ b/radix-engine/src/vm/native_vm.rs @@ -10,9 +10,7 @@ use crate::blueprints::package::PackageNativePackage; use crate::blueprints::pool::v1::package::*; use crate::blueprints::resource::ResourceNativePackage; use crate::blueprints::test_utils::TestUtilsNativePackage; -use crate::blueprints::transaction_processor::{ - TransactionProcessorNativePackage, TransactionProcessorV1MinorVersion, -}; +use crate::blueprints::transaction_processor::{TransactionProcessorNativePackage, TransactionProcessorThread, TransactionProcessorV1MinorVersion}; use crate::blueprints::transaction_tracker::TransactionTrackerNativePackage; use crate::errors::{NativeRuntimeError, RuntimeError, VmError}; use crate::internal_prelude::*; @@ -91,6 +89,11 @@ impl NativeVmInstance { } } +pub enum NativeVmInvokeResult { + Done(V), + SendToChildAndWait, +} + impl VmInvoke for NativeVmInstance { #[trace_resources(log=self.package_address().is_native_package(), log=self.package_address().to_hex(), log=export_name)] fn invoke< diff --git a/radix-engine/src/vm/vm.rs b/radix-engine/src/vm/vm.rs index f87947e759a..aabad2d18da 100644 --- a/radix-engine/src/vm/vm.rs +++ b/radix-engine/src/vm/vm.rs @@ -3,7 +3,7 @@ use crate::errors::{ApplicationError, RuntimeError}; use crate::internal_prelude::*; use crate::kernel::kernel_api::{KernelInternalApi, KernelNodeApi, KernelSubstateApi}; use crate::system::system_callback::{System, SystemLockData}; -use crate::system::system_callback_api::SystemCallbackObject; +use crate::system::system_callback_api::{SystemCallbackObject}; use crate::system::system_substates::KeyValueEntrySubstate; use crate::track::BootStore; use crate::vm::wasm::{ScryptoV1WasmValidator, WasmEngine}; @@ -167,10 +167,10 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' address, &original_code.fully_update_and_into_latest_version().code, )?; - let output = + let result = { vm_instance.invoke(export.export_name.as_str(), input, api, &vm_api)? }; - output + result } VmType::ScryptoV1 => { let instrumented_code = { @@ -209,11 +209,11 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' size: instrumented_code.instrumented_code.len(), })?; - let output = { + let result = { scrypto_vm_instance.invoke(export.export_name.as_str(), input, api, &vm_api)? }; - output + result } }; diff --git a/radix-rust/src/rust.rs b/radix-rust/src/rust.rs index d5afcc47a1d..2a055583193 100644 --- a/radix-rust/src/rust.rs +++ b/radix-rust/src/rust.rs @@ -2,6 +2,7 @@ pub mod prelude { // See eg https://doc.rust-lang.org/std/prelude/index.html // std::prelude::v1 + pub use super::any::Any; pub use super::borrow::ToOwned; pub use super::boxed::Box; pub use super::cell::RefCell; @@ -58,6 +59,8 @@ pub use alloc::sync; #[cfg(feature = "alloc")] pub use alloc::vec; #[cfg(feature = "alloc")] +pub use core::any; +#[cfg(feature = "alloc")] pub use core::cell; #[cfg(feature = "alloc")] pub use core::clone; @@ -97,6 +100,8 @@ pub use std::borrow; #[cfg(not(feature = "alloc"))] pub use std::boxed; #[cfg(not(feature = "alloc"))] +pub use std::any; +#[cfg(not(feature = "alloc"))] pub use std::cell; #[cfg(not(feature = "alloc"))] pub use std::clone; diff --git a/radix-transaction-scenarios/src/executor.rs b/radix-transaction-scenarios/src/executor.rs index 3e8ec16976a..71ab4b2a8d7 100644 --- a/radix-transaction-scenarios/src/executor.rs +++ b/radix-transaction-scenarios/src/executor.rs @@ -379,7 +379,7 @@ where &self.database, VmInit::new(&self.scrypto_vm, self.native_vm_extension.clone()), &self.scenario_execution_config, - &validated.get_executable(), + Rc::new(validated.get_executable()), ); if let TransactionResult::Commit(commit) = &receipt.result { diff --git a/radix-transactions/src/builder/manifest_builder.rs b/radix-transactions/src/builder/manifest_builder.rs index 4d1536aa40d..f82341405fd 100644 --- a/radix-transactions/src/builder/manifest_builder.rs +++ b/radix-transactions/src/builder/manifest_builder.rs @@ -806,6 +806,26 @@ impl ManifestBuilder { self.call_method(address, VALIDATOR_CLAIM_XRD_IDENT, (bucket,)) } + pub fn yield_to_child( + self, + id: Hash, + arguments: impl ResolvableArguments, + ) -> Self { + self.add_instruction(InstructionV1::YieldToChild { + id, + args: arguments.resolve(), + }) + } + + pub fn yield_to_parent( + self, + arguments: impl ResolvableArguments, + ) -> Self { + self.add_instruction(InstructionV1::YieldToParent { + args: arguments.resolve(), + }) + } + /// Calls a scrypto function where the arguments should be one of: /// * A tuple, such as `()`, `(x,)` or `(x, y, z)` /// * IMPORTANT: If calling with a single argument, you must include a trailing comma diff --git a/radix-transactions/src/manifest/decompiler.rs b/radix-transactions/src/manifest/decompiler.rs index 39c7d0d4a7c..8000537b185 100644 --- a/radix-transactions/src/manifest/decompiler.rs +++ b/radix-transactions/src/manifest/decompiler.rs @@ -658,6 +658,8 @@ pub fn decompile_instruction( ))?, ) } + InstructionV1::YieldToChild { .. }=> ("SEND_TO_SUBTRANSACTION_AND_AWAIT", to_manifest_value(&())?), + InstructionV1::YieldToParent { .. }=> ("YIELD", to_manifest_value(&())?), }; write!(f, "{}", display_name)?; diff --git a/radix-transactions/src/model/executable.rs b/radix-transactions/src/model/executable.rs index f92153b8345..5543f28db33 100644 --- a/radix-transactions/src/model/executable.rs +++ b/radix-transactions/src/model/executable.rs @@ -1,11 +1,38 @@ use crate::internal_prelude::*; #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, Default)] -pub struct AuthZoneParams { +pub struct AuthZoneThreadParams { pub initial_proofs: BTreeSet, pub virtual_resources: BTreeSet, } +#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] +pub struct AuthZoneParams { + pub thread_params: Vec, +} + +impl Default for AuthZoneParams { + fn default() -> Self { + Self { + thread_params: vec![AuthZoneThreadParams::default()], + } + } +} + +impl AuthZoneParams { + pub fn single_thread( + initial_proofs: BTreeSet, + virtual_resources: BTreeSet, + ) -> Self { + Self { + thread_params: vec![AuthZoneThreadParams { + initial_proofs, + virtual_resources, + }], + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] pub struct EpochRange { pub start_epoch_inclusive: Epoch, @@ -16,7 +43,6 @@ pub struct EpochRange { pub struct ExecutionContext { pub intent_hash: TransactionIntentHash, pub epoch_range: Option, - pub pre_allocated_addresses: Vec, pub payload_size: usize, pub num_of_signature_validations: usize, pub auth_zone_params: AuthZoneParams, @@ -111,33 +137,44 @@ impl From for TransactionCostingParametersReceipt } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExecutableThread { + pub id: Hash, + pub encoded_instructions: Rc>, + pub references: IndexSet, + pub blobs: Rc>>, + pub pre_allocated_addresses: Vec, +} + /// Executable form of transaction, post stateless validation. #[derive(Debug, PartialEq, Eq)] -pub struct Executable<'a> { - pub(crate) encoded_instructions: &'a [u8], - pub(crate) references: IndexSet, - pub(crate) blobs: &'a IndexMap>, - pub(crate) context: ExecutionContext, - pub(crate) system: bool, +pub struct Executable { + pub threads: Vec, + pub context: ExecutionContext, + pub system: bool, } -impl<'a> Executable<'a> { +impl Executable { pub fn new( - encoded_instructions: &'a [u8], + encoded_instructions: Rc>, references: &IndexSet, - blobs: &'a IndexMap>, + blobs: Rc>>, + pre_allocated_addresses: Vec, context: ExecutionContext, system: bool, ) -> Self { let mut references = references.clone(); - for proof in &context.auth_zone_params.initial_proofs { - references.insert(proof.resource_address().clone().into()); - } - for resource in &context.auth_zone_params.virtual_resources { - references.insert(resource.clone().into()); + for auth_zone_params in &context.auth_zone_params.thread_params { + for proof in &auth_zone_params.initial_proofs { + references.insert(proof.resource_address().clone().into()); + } + for resource in &auth_zone_params.virtual_resources { + references.insert(resource.clone().into()); + } } - for preallocated_address in &context.pre_allocated_addresses { + + for preallocated_address in &pre_allocated_addresses { references.insert( preallocated_address .blueprint_id @@ -147,15 +184,42 @@ impl<'a> Executable<'a> { ); } - Self { + let threads = vec![ExecutableThread { + id: Hash([0u8; Hash::LENGTH]), + pre_allocated_addresses, encoded_instructions, references, blobs, + }]; + + Self { + threads, context, system, } } + pub fn mock() -> Self { + Self { + threads: vec![], + context: ExecutionContext { // TODO: Replace with different system + intent_hash: TransactionIntentHash::NotToCheck { + intent_hash: Hash([0u8; Hash::LENGTH]) + }, + epoch_range: None, + payload_size: 0, + num_of_signature_validations: 0, + auth_zone_params: AuthZoneParams::single_thread(Default::default(), BTreeSet::new()), + costing_parameters: TransactionCostingParameters { + tip_percentage: 0, + free_credit_in_xrd: Decimal::ZERO, + abort_when_loan_repaid: false, + }, + }, + system: false, + } + } + // Consuming builder-like customization methods: pub fn is_system(&self) -> bool { @@ -201,26 +265,14 @@ impl<'a> Executable<'a> { &self.context.costing_parameters } - pub fn blobs(&self) -> &IndexMap> { - &self.blobs - } - - pub fn encoded_instructions(&self) -> &[u8] { - &self.encoded_instructions - } - - pub fn references(&self) -> &IndexSet { - &self.references + pub fn threads(&self) -> &Vec { + &self.threads } pub fn auth_zone_params(&self) -> &AuthZoneParams { &self.context.auth_zone_params } - pub fn pre_allocated_addresses(&self) -> &Vec { - &self.context.pre_allocated_addresses - } - pub fn payload_size(&self) -> usize { self.context.payload_size } diff --git a/radix-transactions/src/model/mod.rs b/radix-transactions/src/model/mod.rs index afb63071786..157a5880a9a 100644 --- a/radix-transactions/src/model/mod.rs +++ b/radix-transactions/src/model/mod.rs @@ -162,16 +162,19 @@ Enum<3u8>( assert_eq!( executable, Executable { - encoded_instructions: &manifest_encode(&manifest.instructions).unwrap(), - references: indexset!( - Reference(FAUCET.into_node_id()), - // NOTE: not needed - Reference(SECP256K1_SIGNATURE_RESOURCE.into_node_id()), - Reference(ED25519_SIGNATURE_RESOURCE.into_node_id()) - ), - blobs: &indexmap!( - hash(&[1, 2]) => vec![1, 2] - ), + threads: vec![ExecutableThread { + encoded_instructions: Rc::new(manifest_encode(&manifest.instructions).unwrap()), + references: indexset!( + Reference(FAUCET.into_node_id()), + // NOTE: not needed + Reference(SECP256K1_SIGNATURE_RESOURCE.into_node_id()), + Reference(ED25519_SIGNATURE_RESOURCE.into_node_id()) + ), + blobs: Rc::new(indexmap!( + hash(&[1, 2]) => vec![1, 2] + )), + pre_allocated_addresses: vec![], + }], context: ExecutionContext { intent_hash: TransactionIntentHash::ToCheck { intent_hash: hash( @@ -206,20 +209,19 @@ Enum<3u8>( start_epoch_inclusive: Epoch::of(55), end_epoch_exclusive: Epoch::of(66) }), - pre_allocated_addresses: vec![], // Source of discrepancy: // * Manifest SBOR payload prefix byte: not counted // * Array header: should be 1 + 1 + len(LEB128(size)), instead of fixed 2 // * Enum variant header: should be 1 + 1 + len(LEB128(size)), instead of fixed 2 payload_size: payload.len() - 3, num_of_signature_validations: 3, - auth_zone_params: AuthZoneParams { - initial_proofs: btreeset!( + auth_zone_params: AuthZoneParams::single_thread( + btreeset!( NonFungibleGlobalId::from_public_key(&sig_1_private_key.public_key()), NonFungibleGlobalId::from_public_key(&sig_2_private_key.public_key()) ), - virtual_resources: btreeset!() - }, + btreeset!() + ), costing_parameters: TransactionCostingParameters { tip_percentage: 4, free_credit_in_xrd: dec!(0), diff --git a/radix-transactions/src/model/v1/instruction.rs b/radix-transactions/src/model/v1/instruction.rs index 9c1a4b564e9..21c88b50def 100644 --- a/radix-transactions/src/model/v1/instruction.rs +++ b/radix-transactions/src/model/v1/instruction.rs @@ -697,6 +697,16 @@ pub enum InstructionV1 { package_address: PackageAddress, blueprint_name: String, }, + + #[sbor(discriminator(INSTRUCTION_YIELD_TO_CHILD_DISCRIMINATOR))] + YieldToChild { + id: Hash, + args: ManifestValue, + }, + #[sbor(discriminator(INSTRUCTION_YIELD_TO_PARENT_DISCRIMINATOR))] + YieldToParent { + args: ManifestValue, + }, } //=============================================================== @@ -765,3 +775,6 @@ pub const INSTRUCTION_CALL_DIRECT_VAULT_METHOD_DISCRIMINATOR: u8 = 0x45; pub const INSTRUCTION_DROP_NAMED_PROOFS_DISCRIMINATOR: u8 = 0x52; pub const INSTRUCTION_DROP_ALL_PROOFS_DISCRIMINATOR: u8 = 0x50; pub const INSTRUCTION_ALLOCATE_GLOBAL_ADDRESS_DISCRIMINATOR: u8 = 0x51; + +pub const INSTRUCTION_YIELD_TO_CHILD_DISCRIMINATOR: u8 = 0x53; +pub const INSTRUCTION_YIELD_TO_PARENT_DISCRIMINATOR: u8 = 0x54; diff --git a/radix-transactions/src/model/v1/preview_transaction.rs b/radix-transactions/src/model/v1/preview_transaction.rs index 590aedf7c35..3d2334c3e3b 100644 --- a/radix-transactions/src/model/v1/preview_transaction.rs +++ b/radix-transactions/src/model/v1/preview_transaction.rs @@ -24,7 +24,7 @@ pub struct ValidatedPreviewIntent { } impl ValidatedPreviewIntent { - pub fn get_executable<'a>(&'a self) -> Executable<'a> { + pub fn get_executable(&self) -> Executable { let intent = &self.intent; let flags = &self.flags; @@ -55,9 +55,10 @@ impl ValidatedPreviewIntent { let intent_hash = intent.intent_hash(); Executable::new( - &self.encoded_instructions, + Rc::new(self.encoded_instructions.clone()), &intent.instructions.references, - &intent.blobs.blobs_by_hash, + Rc::new(intent.blobs.blobs_by_hash.clone()), + vec![], ExecutionContext { intent_hash: if flags.skip_epoch_check { TransactionIntentHash::NotToCheck { @@ -79,12 +80,8 @@ impl ValidatedPreviewIntent { }, payload_size: self.intent.summary.effective_length, num_of_signature_validations: 0, // Accounted for by tests in `common_transformation_costs.rs`. - auth_zone_params: AuthZoneParams { - initial_proofs, - virtual_resources, - }, + auth_zone_params: AuthZoneParams::single_thread(initial_proofs, virtual_resources), costing_parameters: fee_payment, - pre_allocated_addresses: vec![], }, false, ) diff --git a/radix-transactions/src/model/v1/system_transaction.rs b/radix-transactions/src/model/v1/system_transaction.rs index e4f485b8f7a..2a0bbd27028 100644 --- a/radix-transactions/src/model/v1/system_transaction.rs +++ b/radix-transactions/src/model/v1/system_transaction.rs @@ -96,14 +96,15 @@ impl SystemTransactionV1 { } impl PreparedSystemTransactionV1 { - pub fn get_executable<'a>( - &'a self, + pub fn get_executable( + &self, initial_proofs: BTreeSet, - ) -> Executable<'a> { + ) -> Executable { Executable::new( - &self.encoded_instructions, + Rc::new(self.encoded_instructions.clone()), &self.references, - &self.blobs.blobs_by_hash, + Rc::new(self.blobs.blobs_by_hash.clone()), + self.pre_allocated_addresses.inner.clone(), ExecutionContext { intent_hash: TransactionIntentHash::NotToCheck { intent_hash: self.hash_for_execution.hash, @@ -111,16 +112,12 @@ impl PreparedSystemTransactionV1 { epoch_range: None, payload_size: 0, num_of_signature_validations: 0, - auth_zone_params: AuthZoneParams { - initial_proofs, - virtual_resources: BTreeSet::new(), - }, + auth_zone_params: AuthZoneParams::single_thread(initial_proofs, BTreeSet::new()), costing_parameters: TransactionCostingParameters { tip_percentage: 0, free_credit_in_xrd: Decimal::ZERO, abort_when_loan_repaid: false, }, - pre_allocated_addresses: self.pre_allocated_addresses.inner.clone(), }, true, ) diff --git a/radix-transactions/src/model/v1/test_transaction.rs b/radix-transactions/src/model/v1/test_transaction.rs index ca614745546..dd4e51de5c3 100644 --- a/radix-transactions/src/model/v1/test_transaction.rs +++ b/radix-transactions/src/model/v1/test_transaction.rs @@ -47,14 +47,15 @@ impl TestTransaction { } impl PreparedTestTransaction { - pub fn get_executable<'a>( - &'a self, + pub fn get_executable( + &self, initial_proofs: BTreeSet, - ) -> Executable<'a> { + ) -> Executable { Executable::new( - &self.encoded_instructions, + Rc::new(self.encoded_instructions.clone()), &self.references, - &self.blobs, + Rc::new(self.blobs.clone()), + vec![], ExecutionContext { intent_hash: TransactionIntentHash::NotToCheck { intent_hash: self.hash, @@ -64,16 +65,12 @@ impl PreparedTestTransaction { + self.blobs.values().map(|x| x.len()).sum::(), // For testing purpose, assume `num_of_signature_validations = num_of_initial_proofs + 1` num_of_signature_validations: initial_proofs.len() + 1, - auth_zone_params: AuthZoneParams { - initial_proofs, - virtual_resources: BTreeSet::new(), - }, + auth_zone_params: AuthZoneParams::single_thread(initial_proofs, BTreeSet::new()), costing_parameters: TransactionCostingParameters { tip_percentage: DEFAULT_TIP_PERCENTAGE, free_credit_in_xrd: Decimal::ZERO, abort_when_loan_repaid: false, }, - pre_allocated_addresses: vec![], }, false, ) diff --git a/radix-transactions/src/model/v1/validated_notarized_transaction.rs b/radix-transactions/src/model/v1/validated_notarized_transaction.rs index a6e385e2110..c2beffcc1c4 100644 --- a/radix-transactions/src/model/v1/validated_notarized_transaction.rs +++ b/radix-transactions/src/model/v1/validated_notarized_transaction.rs @@ -28,16 +28,17 @@ impl HasNotarizedTransactionHash for ValidatedNotarizedTransactionV1 { } impl ValidatedNotarizedTransactionV1 { - pub fn get_executable<'a>(&'a self) -> Executable<'a> { + pub fn get_executable(&self) -> Executable { let intent = &self.prepared.signed_intent.intent; let header = &intent.header.inner; let intent_hash = intent.intent_hash(); let summary = &self.prepared.summary; Executable::new( - &self.encoded_instructions, + Rc::new(self.encoded_instructions.clone()), &intent.instructions.references, - &intent.blobs.blobs_by_hash, + Rc::new(intent.blobs.blobs_by_hash.clone()), + vec![], ExecutionContext { intent_hash: TransactionIntentHash::ToCheck { intent_hash: intent_hash.into_hash(), @@ -49,16 +50,15 @@ impl ValidatedNotarizedTransactionV1 { }), payload_size: summary.effective_length, num_of_signature_validations: self.num_of_signature_validations, - auth_zone_params: AuthZoneParams { - initial_proofs: AuthAddresses::signer_set(&self.signer_keys), - virtual_resources: BTreeSet::new(), - }, + auth_zone_params: AuthZoneParams::single_thread( + AuthAddresses::signer_set(&self.signer_keys), + BTreeSet::new(), + ), costing_parameters: TransactionCostingParameters { tip_percentage: intent.header.inner.tip_percentage, free_credit_in_xrd: Decimal::ZERO, abort_when_loan_repaid: false, }, - pre_allocated_addresses: vec![], }, false, ) diff --git a/radix-transactions/src/validation/transaction_validator.rs b/radix-transactions/src/validation/transaction_validator.rs index 73febd26561..eecd2150b2c 100644 --- a/radix-transactions/src/validation/transaction_validator.rs +++ b/radix-transactions/src/validation/transaction_validator.rs @@ -261,7 +261,9 @@ impl NotarizedTransactionValidator { | InstructionV1::CallRoyaltyMethod { args, .. } | InstructionV1::CallMetadataMethod { args, .. } | InstructionV1::CallRoleAssignmentMethod { args, .. } - | InstructionV1::CallDirectVaultMethod { args, .. } => { + | InstructionV1::CallDirectVaultMethod { args, .. } + | InstructionV1::YieldToParent { args } + | InstructionV1::YieldToChild { args, .. } => { Self::validate_call_args(&args, &mut id_validator) .map_err(TransactionValidationError::CallDataValidationError)?; } diff --git a/scrypto-test/src/environment/builder.rs b/scrypto-test/src/environment/builder.rs index 8312e27db5e..5dc878b889b 100644 --- a/scrypto-test/src/environment/builder.rs +++ b/scrypto-test/src/environment/builder.rs @@ -250,10 +250,10 @@ where Self::DEFAULT_INTENT_HASH, ); - let auth_module = AuthModule::new(AuthZoneParams { - initial_proofs: Default::default(), - virtual_resources: Default::default(), - }); + let auth_module = AuthModule::new(AuthZoneParams::single_thread( + Default::default(), + Default::default(), + )); let limits_module = LimitsModule::from_params(LimitParameters::babylon_genesis()); @@ -270,6 +270,7 @@ where }; System { + executable: Rc::new(Executable::mock()), blueprint_cache: NonIterMap::new(), auth_cache: NonIterMap::new(), schema_cache: NonIterMap::new(), @@ -304,7 +305,7 @@ where pinned_to_heap: Default::default(), }, id_allocator, - CallFrame::new_root(Actor::Root), + CallFrame::new_root(Actor::Root, Default::default()), vec![], system_config, ) diff --git a/scrypto-test/src/environment/system_api.rs b/scrypto-test/src/environment/system_api.rs index 00089e9ae74..b0ef54b4f78 100644 --- a/scrypto-test/src/environment/system_api.rs +++ b/scrypto-test/src/environment/system_api.rs @@ -4,6 +4,7 @@ //! [`SystemApi`]: crate::prelude::SystemApi //! [`TestEnvironment`]: crate::prelude::TestEnvironment +use radix_engine_interface::api::thread_api::SystemThreadApi; use crate::prelude::*; /// Implements the [`SystemApi`] for the [`TestEnvironment`] struct. @@ -102,6 +103,11 @@ macro_rules! implement_system_api { } implement_system_api! { SystemApi: {}, + SystemThreadApi: { + move_to_stack: (&mut self, thread: usize, value: IndexedScryptoValue) -> Result<(), RuntimeError>, + switch_stack: (&mut self, thread: usize) -> Result<(), RuntimeError>, + free_stack: (&mut self, thread: usize) -> Result<(), RuntimeError>, + }, SystemActorApi: { actor_get_blueprint_id: (&mut self) -> Result, actor_open_field: ( diff --git a/scrypto-test/src/ledger_simulator/inject_costing_err.rs b/scrypto-test/src/ledger_simulator/inject_costing_err.rs index 7b73570f3c1..27c41b336ca 100644 --- a/scrypto-test/src/ledger_simulator/inject_costing_err.rs +++ b/scrypto-test/src/ledger_simulator/inject_costing_err.rs @@ -1,17 +1,9 @@ use radix_common::prelude::*; -use radix_engine::errors::BootloadingError; use radix_engine::errors::{RejectionReason, TransactionExecutionError}; use radix_engine::errors::{RuntimeError, SystemModuleError}; -use radix_engine::kernel::call_frame::{CallFrameMessage, NodeVisibility, StableReferenceType}; -use radix_engine::kernel::kernel_api::{ - DroppedNode, KernelApi, KernelInternalApi, KernelInvocation, KernelInvokeApi, KernelNodeApi, - KernelSubstateApi, SystemState, -}; -use radix_engine::kernel::kernel_callback_api::{ - CloseSubstateEvent, CreateNodeEvent, DrainSubstatesEvent, DropNodeEvent, KernelCallbackObject, - MoveModuleEvent, OpenSubstateEvent, ReadSubstateEvent, RemoveSubstateEvent, ScanKeysEvent, - ScanSortedSubstatesEvent, SetSubstateEvent, WriteSubstateEvent, -}; +use radix_engine::kernel::call_frame::{CallFrameMessage, NodeVisibility, RootCallFrameInitRefs, StableReferenceType}; +use radix_engine::kernel::kernel_api::{DroppedNode, KernelApi, KernelInternalApi, KernelInvocation, KernelInvokeApi, KernelNodeApi, KernelSubstateApi, KernelThreadApi, SystemState}; +use radix_engine::kernel::kernel_callback_api::{CloseSubstateEvent, CreateNodeEvent, DrainSubstatesEvent, DropNodeEvent, KernelCallbackObject, MoveModuleEvent, OpenSubstateEvent, ReadSubstateEvent, RemoveSubstateEvent, ScanKeysEvent, ScanSortedSubstatesEvent, SetSubstateEvent, WriteSubstateEvent}; use radix_engine::system::actor::Actor; use radix_engine::system::system_callback::{System, SystemInit, SystemLockData}; use radix_engine::system::system_callback_api::SystemCallbackObject; @@ -27,8 +19,7 @@ use radix_engine_interface::blueprints::transaction_processor::InstructionOutput use radix_engine_interface::prelude::*; use radix_substate_store_interface::db_key_mapper::{SpreadPrefixKeyMapper, SubstateKeyContent}; use radix_substate_store_interface::interface::SubstateDatabase; -use radix_transactions::model::Executable; -use radix_transactions::prelude::PreAllocatedAddress; +use radix_transactions::model::{Executable, ExecutableThread}; pub type InjectSystemCostingError<'a, E> = InjectCostingError>; @@ -94,42 +85,24 @@ impl KernelCallbackObject for InjectCostingError { fn init( store: &mut S, - executable: &Executable, + executable: Rc, init_input: Self::Init, - ) -> Result { - let mut system = System::::init(store, executable, init_input.system_input)?; + ) -> Result<(Self, Vec), RejectionReason> { + let (mut system, init_call_frames) = System::::init(store, executable, init_input.system_input)?; let fail_after = Rc::new(RefCell::new(init_input.error_after_count)); system.modules.costing_mut().unwrap().on_apply_cost = OnApplyCost::ForceFailOnCount { fail_after: fail_after.clone(), }; - Ok(Self { fail_after, system }) - } - - fn verify_boot_ref_value( - &mut self, - node_id: &NodeId, - value: &IndexedScryptoValue, - ) -> Result { - self.system.verify_boot_ref_value(node_id, value) + Ok((Self { fail_after, system }, init_call_frames)) } fn start>( api: &mut Y, - manifest_encoded_instructions: &[u8], - pre_allocated_addresses: &Vec, - references: &IndexSet, - blobs: &IndexMap>, ) -> Result, RuntimeError> { let mut api = wrapped_api!(api); - System::start( - &mut api, - manifest_encoded_instructions, - pre_allocated_addresses, - references, - blobs, - ) + System::start(&mut api) } fn finish(&mut self, store_commit_info: StoreCommitInfo) -> Result<(), RuntimeError> { @@ -140,10 +113,9 @@ impl KernelCallbackObject for InjectCostingError { fn create_receipt( self, track: Track, - executable: &Executable, result: Result, TransactionExecutionError>, ) -> TransactionReceipt { - self.system.create_receipt(track, executable, result) + self.system.create_receipt(track, result) } fn on_pin_node(&mut self, node_id: &NodeId) -> Result<(), RuntimeError> { @@ -336,6 +308,22 @@ pub struct WrappedKernelApi<'a, M: SystemCallbackObject + 'a, K: KernelApi, } +impl<'a, M: SystemCallbackObject, K: KernelApi>> KernelThreadApi> +for WrappedKernelApi<'a, M, K> +{ + fn kernel_send(&mut self, thread: usize, value: IndexedScryptoValue) -> Result<(), RuntimeError> { + self.api.kernel_send(thread, value) + } + + fn kernel_switch_context(&mut self, thread: usize) -> Result<(), RuntimeError> { + self.api.kernel_switch_context(thread) + } + + fn kernel_update_call_frame_data(&mut self, update: Actor) -> Result<(), RuntimeError> { + self.api.kernel_update_call_frame_data(update) + } +} + impl<'a, M: SystemCallbackObject, K: KernelApi>> KernelNodeApi for WrappedKernelApi<'a, M, K> { @@ -505,6 +493,10 @@ impl<'a, M: SystemCallbackObject, K: KernelApi>> KernelInt self.api.kernel_get_current_depth() } + fn kernel_get_current_thread(&self) -> usize { + self.api.kernel_get_current_thread() + } + fn kernel_get_node_visibility(&self, node_id: &NodeId) -> NodeVisibility { self.api.kernel_get_node_visibility(node_id) } @@ -548,6 +540,10 @@ impl<'a, M: SystemCallbackObject, K: KernelInternalApi>> self.api.kernel_get_current_depth() } + fn kernel_get_current_thread(&self) -> usize { + self.api.kernel_get_current_thread() + } + fn kernel_get_node_visibility(&self, node_id: &NodeId) -> NodeVisibility { self.api.kernel_get_node_visibility(node_id) } diff --git a/scrypto-test/src/ledger_simulator/ledger_simulator.rs b/scrypto-test/src/ledger_simulator/ledger_simulator.rs index fca8bd49b64..b50066bb882 100644 --- a/scrypto-test/src/ledger_simulator/ledger_simulator.rs +++ b/scrypto-test/src/ledger_simulator/ledger_simulator.rs @@ -1387,7 +1387,7 @@ impl LedgerSimulator { }, ); - let transaction_receipt = executor.execute(&executable); + let transaction_receipt = executor.execute(Rc::new(executable)); if let TransactionResult::Commit(commit) = &transaction_receipt.result { let database_updates = commit @@ -1466,7 +1466,7 @@ impl LedgerSimulator { _, Vm<'_, DefaultWasmEngine, E>, >( - &mut self.database, vm_init, &execution_config, &executable + &mut self.database, vm_init, &execution_config, Rc::new(executable) ); if let TransactionResult::Commit(commit) = &transaction_receipt.result { let database_updates = commit @@ -1923,9 +1923,10 @@ impl LedgerSimulator { receipt.expect_commit(true).new_resource_addresses()[0] } - pub fn create_mintable_burnable_fungible_resource( + pub fn create_mintable_burnable_fungible_resource_with_initial_amount( &mut self, account: ComponentAddress, + amount: Option, ) -> (ResourceAddress, ResourceAddress) { let admin_auth = self.create_non_fungible_resource(account); @@ -1947,7 +1948,7 @@ impl LedgerSimulator { ..Default::default() }, metadata!(), - None, + amount, ) .try_deposit_entire_worktop_or_abort(account, None) .build(); @@ -1956,6 +1957,13 @@ impl LedgerSimulator { (admin_auth, resource_address) } + pub fn create_mintable_burnable_fungible_resource( + &mut self, + account: ComponentAddress, + ) -> (ResourceAddress, ResourceAddress) { + self.create_mintable_burnable_fungible_resource_with_initial_amount(account, None) + } + pub fn create_freely_mintable_fungible_resource( &mut self, owner_role: OwnerRole,