From 27c5f388429dcfb58a53c7bc5647d336d75d87bd Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Mon, 9 Sep 2024 15:52:38 -0500 Subject: [PATCH 01/12] Add multiple stacks --- radix-engine/src/kernel/kernel.rs | 334 +++++++++++++++++------- scrypto-test/src/environment/builder.rs | 2 - 2 files changed, 237 insertions(+), 99 deletions(-) diff --git a/radix-engine/src/kernel/kernel.rs b/radix-engine/src/kernel/kernel.rs index db9e6d84061..6852367ffe2 100644 --- a/radix-engine/src/kernel/kernel.rs +++ b/radix-engine/src/kernel/kernel.rs @@ -17,9 +17,10 @@ use sbor::rust::mem; macro_rules! as_read_only { ($kernel:expr) => {{ + let (current_frame, prev_frame) = $kernel.stacks.cur_and_prev(); KernelReadOnly { - current_frame: &$kernel.current_frame, - prev_frame: $kernel.prev_frame_stack.last(), + current_frame, + prev_frame, heap: &$kernel.substate_io.heap, callback: $kernel.callback, } @@ -104,12 +105,7 @@ impl<'h, M: KernelTransactionCallbackObject, S: SubstateDatabase> BootLoader<'h, &mut self.track, &mut self.id_allocator, &mut system, - // TODO: Fix to take call frame inits for each intent - { - let mut call_frame_inits = call_frame_inits; - let first_call_frame_init = call_frame_inits.drain(..).next().unwrap(); - first_call_frame_init - }, + call_frame_inits, ); // Execution @@ -118,7 +114,9 @@ impl<'h, M: KernelTransactionCallbackObject, S: SubstateDatabase> BootLoader<'h, let output = M::start(&mut kernel, executable)?; // Sanity check call frame - assert!(kernel.prev_frame_stack.is_empty()); + for stack in &kernel.stacks.stacks { + assert!(stack.prev_frames.is_empty()); + } // Sanity check heap assert!(kernel.substate_io.heap.is_empty()); @@ -136,6 +134,125 @@ impl<'h, M: KernelTransactionCallbackObject, S: SubstateDatabase> BootLoader<'h, } } +pub struct KernelStack { + current_frame: CallFrame, + prev_frames: Vec>, +} + +impl KernelStack { + pub fn new(init: CallFrameInit) -> Self { + Self { + current_frame: CallFrame::new_root(init), + prev_frames: vec![], + } + } +} + +pub struct KernelStacks { + stack_pointer: usize, + stacks: Vec>, +} + +impl KernelStacks { + pub fn new(call_frames: Vec>) -> Self { + let stacks = call_frames + .into_iter() + .map(|call_frame| KernelStack::new(call_frame)) + .collect(); + Self { + stack_pointer: 0usize, + stacks, + } + } + + pub fn push(&mut self, frame: CallFrame) { + let stack = self.stacks.get_mut(self.stack_pointer).unwrap(); + let parent = mem::replace(&mut stack.current_frame, frame); + stack.prev_frames.push(parent); + } + + pub fn pop(&mut self) { + let stack = self.stacks.get_mut(self.stack_pointer).unwrap(); + let parent = stack.prev_frames.pop().unwrap(); + let _ = core::mem::replace(&mut stack.current_frame, parent); + } + + pub fn switch(&mut self, id: usize) { + self.stack_pointer = id; + } + + pub fn cur_mut_and_other_mut( + &mut self, + other_stack: usize, + ) -> ( + &mut CallFrame, + &mut CallFrame, + ) { + let mut mut_stacks: Vec<_> = self + .stacks + .iter_mut() + .enumerate() + .filter(|(id, _)| (*id).eq(&self.stack_pointer) || (*id).eq(&other_stack)) + .map(|stack| Some(stack)) + .collect(); + + let (id0, stack0) = mut_stacks[0].take().unwrap(); + let (_id1, stack1) = mut_stacks[1].take().unwrap(); + if id0.eq(&self.stack_pointer) { + (&mut stack0.current_frame, &mut stack1.current_frame) + } else { + (&mut stack1.current_frame, &mut stack0.current_frame) + } + } + + pub fn cur_and_prev( + &self, + ) -> ( + &CallFrame, + Option<&CallFrame>, + ) { + let stack = self.stacks.get(self.stack_pointer).unwrap(); + (&stack.current_frame, stack.prev_frames.last()) + } + + pub fn cur_mut_and_prev( + &mut self, + ) -> ( + &mut CallFrame, + Option<&CallFrame>, + ) { + let stack = self.stacks.get_mut(self.stack_pointer).unwrap(); + (&mut stack.current_frame, stack.prev_frames.last()) + } + + pub fn cur_mut_and_prev_mut( + &mut self, + ) -> ( + &mut CallFrame, + Option<&mut CallFrame>, + ) { + let stack = self.stacks.get_mut(self.stack_pointer).unwrap(); + (&mut stack.current_frame, stack.prev_frames.last_mut()) + } + + pub fn cur(&self) -> &CallFrame { + &self.stacks.get(self.stack_pointer).unwrap().current_frame + } + + pub fn cur_mut(&mut self) -> &mut CallFrame { + &mut self + .stacks + .get_mut(self.stack_pointer) + .unwrap() + .current_frame + } + + pub fn prev_frames_mut(&mut self) -> &mut Vec> { + let stack = self.stacks.get_mut(self.stack_pointer).unwrap(); + &mut stack.prev_frames + } +} + pub struct Kernel< 'g, // Lifetime of values outliving all frames M, // Upstream System layer @@ -144,12 +261,7 @@ 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>, + stacks: KernelStacks, substate_io: SubstateIO<'g, S>, @@ -171,7 +283,16 @@ impl< id_allocator: &'g mut IdAllocator, callback: &'g mut M, ) -> Self { - Self::new(store, id_allocator, callback, Default::default()) + Self::new( + store, + id_allocator, + callback, + vec![CallFrameInit { + data: M::CallFrameData::default(), + direct_accesses: Default::default(), + global_addresses: Default::default(), + }], + ) } } @@ -180,9 +301,10 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore + BootStore> Kernel store: &'g mut S, id_allocator: &'g mut IdAllocator, callback: &'g mut M, - call_frame_init: CallFrameInit, + call_frame_inits: Vec>, ) -> Self { Kernel { + stacks: KernelStacks::new(call_frame_inits), substate_io: SubstateIO { heap: Heap::new(), store, @@ -192,8 +314,6 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore + BootStore> Kernel pinned_to_heap: BTreeSet::new(), }, id_allocator, - current_frame: CallFrame::new_root(call_frame_init), - prev_frame_stack: vec![], callback, } } @@ -273,7 +393,8 @@ where fn kernel_pin_node(&mut self, node_id: NodeId) -> Result<(), RuntimeError> { M::on_pin_node(&node_id, &mut as_read_only!(self))?; - self.current_frame + self.stacks + .cur_mut() .pin_node(&mut self.substate_io, node_id) .map_err(|e| { RuntimeError::KernelError(KernelError::CallFrameError( @@ -301,15 +422,17 @@ where &mut read_only, )?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_create_node(CreateNodeEvent::IOAccess(&io_access), api) }, }; - self.current_frame + cur_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( @@ -338,15 +461,17 @@ where &mut read_only, )?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_create_node(CreateNodeEvent::IOAccess(&io_access), api) }, }; - self.current_frame + cur_frame .create_node( &mut self.substate_io, &mut handler, @@ -365,16 +490,18 @@ where } { + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_move_module(MoveModuleEvent::IOAccess(&io_access), api) }, }; for (dest_partition_number, (src_node_id, src_partition_number)) in partitions { - self.current_frame + cur_frame .move_partition( &mut self.substate_io, &mut handler, @@ -402,15 +529,16 @@ where M::on_drop_node_mut(node_id, self)?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_drop_node(DropNodeEvent::IOAccess(&io_access), api) }, }; - let dropped_node = self - .current_frame + let dropped_node = cur_frame .drop_node(&mut self.substate_io, node_id, &mut handler) .map_err(|e| match e { CallbackError::Error(e) => RuntimeError::KernelError(KernelError::CallFrameError( @@ -441,7 +569,7 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelInternalApi type System = M; fn kernel_get_node_visibility(&self, node_id: &NodeId) -> NodeVisibility { - self.current_frame.get_node_visibility(node_id) + self.stacks.cur().get_node_visibility(node_id) } fn kernel_get_thread_id(&self) -> usize { @@ -450,20 +578,21 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelInternalApi } fn kernel_get_current_depth(&self) -> usize { - self.current_frame.depth() + self.stacks.cur().depth() } fn kernel_get_system_state(&mut self) -> SystemState<'_, M> { - let caller_actor = match self.prev_frame_stack.last() { + let (cur, prev) = self.stacks.cur_and_prev(); + let caller_actor = match prev { Some(call_frame) => call_frame.data(), None => { // This will only occur on initialization - self.current_frame.data() + cur.data() } }; SystemState { system: &mut self.callback, - current_call_frame: self.current_frame.data(), + current_call_frame: cur.data(), caller_call_frame: caller_actor, } } @@ -545,7 +674,8 @@ where ) -> Result<(), RuntimeError> { M::on_mark_substate_as_transient(&node_id, &partition_num, &key, &mut as_read_only!(self))?; - self.current_frame + self.stacks + .cur_mut() .mark_substate_as_transient(&mut self.substate_io, node_id, partition_num, key) .map_err(|e| { RuntimeError::KernelError(KernelError::CallFrameError( @@ -574,15 +704,17 @@ where &mut as_read_only!(self), )?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_open_substate(OpenSubstateEvent::IOAccess(&io_access), api) }, }; - let maybe_lock_handle = self.current_frame.open_substate( + let maybe_lock_handle = cur_frame.open_substate( &mut self.substate_io, node_id, partition_num, @@ -601,15 +733,17 @@ where M::on_substate_lock_fault(*node_id, partition_num, &substate_key, self)?; if retry { + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_open_substate(OpenSubstateEvent::IOAccess(&io_access), api) }, }; - self.current_frame + cur_frame .open_substate( &mut self.substate_io, &node_id, @@ -666,7 +800,8 @@ where &mut self, lock_handle: SubstateHandle, ) -> Result { - self.current_frame + self.stacks + .cur() .get_handle_info(lock_handle) .ok_or(RuntimeError::KernelError( KernelError::SubstateHandleDoesNotExist(lock_handle), @@ -678,16 +813,16 @@ where &mut self, lock_handle: SubstateHandle, ) -> Result<&IndexedScryptoValue, RuntimeError> { + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_read_substate(ReadSubstateEvent::IOAccess(&io_access), api) }, }; - let value = self - .current_frame + let value = cur_frame .read_substate(&mut self.substate_io, lock_handle, &mut handler) .map_err(|e| match e { CallbackError::Error(e) => RuntimeError::KernelError(KernelError::CallFrameError( @@ -714,15 +849,17 @@ where &mut read_only, )?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_write_substate(WriteSubstateEvent::IOAccess(&io_access), api) }, }; - self.current_frame + cur_frame .write_substate(&mut self.substate_io, lock_handle, value, &mut handler) .map_err(|e| match e { CallbackError::Error(e) => RuntimeError::KernelError(KernelError::CallFrameError( @@ -743,7 +880,8 @@ where let mut read_only = as_read_only!(self); M::on_close_substate(CloseSubstateEvent::Start(lock_handle), &mut read_only)?; - self.current_frame + self.stacks + .cur_mut() .close_substate(&mut self.substate_io, lock_handle) .map_err(|e| { RuntimeError::KernelError(KernelError::CallFrameError( @@ -767,15 +905,17 @@ where &mut as_read_only!(self), )?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_set_substate(SetSubstateEvent::IOAccess(&io_access), api) }, }; - self.current_frame + cur_frame .set_substate( &mut self.substate_io, node_id, @@ -806,16 +946,17 @@ where &mut as_read_only!(self), )?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_remove_substate(RemoveSubstateEvent::IOAccess(&io_access), api) }, }; - let substate = self - .current_frame + let substate = cur_frame .remove_substate( &mut self.substate_io, node_id, @@ -842,16 +983,17 @@ where ) -> Result, RuntimeError> { M::on_scan_keys(ScanKeysEvent::Start, &mut as_read_only!(self))?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_scan_keys(ScanKeysEvent::IOAccess(&io_access), api) }, }; - let keys = self - .current_frame + let keys = cur_frame .scan_keys::( &mut self.substate_io, node_id, @@ -878,16 +1020,17 @@ where ) -> Result, RuntimeError> { M::on_drain_substates(DrainSubstatesEvent::Start(limit), &mut as_read_only!(self))?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_drain_substates(DrainSubstatesEvent::IOAccess(&io_access), api) }, }; - let substates = self - .current_frame + let substates = cur_frame .drain_substates::( &mut self.substate_io, node_id, @@ -914,16 +1057,17 @@ where ) -> Result, RuntimeError> { M::on_scan_sorted_substates(ScanSortedSubstatesEvent::Start, &mut as_read_only!(self))?; + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev(); + let mut handler = KernelHandler { callback: self.callback, - prev_frame: self.prev_frame_stack.last(), + prev_frame, on_io_access: |api, io_access| { M::on_scan_sorted_substates(ScanSortedSubstatesEvent::IOAccess(&io_access), api) }, }; - let substates = self - .current_frame + let substates = cur_frame .scan_sorted( &mut self.substate_io, node_id, @@ -963,14 +1107,14 @@ where { let frame = CallFrame::new_child_from_parent( &self.substate_io, - &mut self.current_frame, + self.stacks.cur_mut(), 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); + + self.stacks.push(frame); } // Execute @@ -979,10 +1123,11 @@ where M::on_execution_start(self)?; // Auto drop locks - for handle in self.current_frame.open_substates() { + for handle in self.stacks.cur().open_substates() { M::on_close_substate(CloseSubstateEvent::Start(handle), self)?; } - self.current_frame + self.stacks + .cur_mut() .close_all_substates(&mut self.substate_io); // Run @@ -990,10 +1135,11 @@ 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.stacks.cur().open_substates() { M::on_close_substate(CloseSubstateEvent::Start(handle), self)?; } - self.current_frame + self.stacks + .cur_mut() .close_all_substates(&mut self.substate_io); // Handle execution finish @@ -1004,24 +1150,24 @@ where // Move { - let parent = self.prev_frame_stack.last_mut().unwrap(); + let (cur_frame, prev_frame) = self.stacks.cur_mut_and_prev_mut(); // Move resource CallFrame::pass_message( &self.substate_io, - &mut self.current_frame, - parent, + cur_frame, + prev_frame.unwrap(), message.clone(), ) .map_err(CallFrameError::PassMessageError) .map_err(KernelError::CallFrameError)?; // Auto-drop - let owned_nodes = self.current_frame.owned_nodes(); + let owned_nodes = cur_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.stacks.cur().owned_nodes(); if !owned_nodes.is_empty() { return Err(RuntimeError::KernelError(KernelError::OrphanedNodes( owned_nodes, @@ -1030,10 +1176,7 @@ where } // Pop call frame - { - let parent = self.prev_frame_stack.pop().unwrap(); - let _ = core::mem::replace(&mut self.current_frame, parent); - } + self.stacks.pop(); M::after_invoke(&output, self)?; @@ -1044,12 +1187,12 @@ where impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelThreadApi for Kernel<'g, M, S> { type CallFrameData = M::CallFrameData; fn kernel_set_call_frame_data(&mut self, data: M::CallFrameData) -> Result<(), RuntimeError> { - *self.current_frame.data_mut() = data; + *self.stacks.cur_mut().data_mut() = data; Ok(()) } fn kernel_get_owned_nodes(&mut self) -> Result, RuntimeError> { - Ok(self.current_frame.owned_nodes()) + Ok(self.stacks.cur().owned_nodes()) } } @@ -1058,28 +1201,36 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelApi for Kern } #[cfg(feature = "radix_engine_tests")] -impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> Kernel<'g, M, S> { +impl<'g, M, S> Kernel<'g, M, S> +where + M: KernelCallbackObject, + S: CommitableSubstateStore, +{ pub fn kernel_create_kernel_for_testing( substate_io: SubstateIO<'g, S>, id_allocator: &'g mut IdAllocator, - current_frame: CallFrame, - prev_frame_stack: Vec>, callback: &'g mut M, ) -> Kernel<'g, M, S> { Self { - current_frame, - prev_frame_stack, + stacks: KernelStacks::new(vec![CallFrameInit { + data: M::CallFrameData::default(), + direct_accesses: Default::default(), + global_addresses: Default::default(), + }]), substate_io, id_allocator, callback, } } +} +#[cfg(feature = "radix_engine_tests")] +impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> Kernel<'g, M, S> { pub fn kernel_current_frame( &self, ) -> &CallFrame<::CallFrameData, ::LockData> { - &self.current_frame + self.stacks.cur() } pub fn kernel_current_frame_mut( @@ -1091,18 +1242,7 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> Kernel<'g, M, S> { ::LockData, >, ) { - (&self.substate_io, &mut self.current_frame) - } - - pub fn kernel_prev_frame_stack( - &self, - ) -> &Vec< - CallFrame< - ::CallFrameData, - ::LockData, - >, - > { - &self.prev_frame_stack + (&self.substate_io, self.stacks.cur_mut()) } pub fn kernel_prev_frame_stack_mut( @@ -1113,7 +1253,7 @@ impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> Kernel<'g, M, S> { ::LockData, >, > { - &mut self.prev_frame_stack + self.stacks.prev_frames_mut() } pub fn kernel_substate_io(&self) -> &SubstateIO<'g, S> { diff --git a/scrypto-test/src/environment/builder.rs b/scrypto-test/src/environment/builder.rs index cf356fb37c7..eced465acad 100644 --- a/scrypto-test/src/environment/builder.rs +++ b/scrypto-test/src/environment/builder.rs @@ -282,8 +282,6 @@ where pinned_to_heap: Default::default(), }, id_allocator, - CallFrame::new_root(Default::default()), - vec![], system_config, ) }, From 424b04f14a8fcb31567d451177c34bf52cdd83f5 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Mon, 9 Sep 2024 16:08:38 -0500 Subject: [PATCH 02/12] Add kernel switch thread api --- radix-engine-tests/tests/kernel/panics.rs | 5 ++ radix-engine-tests/tests/system/mod.rs | 2 + .../transaction_processor/instructions.rs | 51 ++++++++++++++++++- radix-engine/src/kernel/kernel.rs | 6 +++ radix-engine/src/kernel/kernel_api.rs | 5 ++ radix-engine/src/system/system_callback.rs | 8 ++- .../src/model/v2/child_intents_v2.rs | 2 +- .../ledger_simulator/inject_costing_err.rs | 5 ++ 8 files changed, 80 insertions(+), 4 deletions(-) diff --git a/radix-engine-tests/tests/kernel/panics.rs b/radix-engine-tests/tests/kernel/panics.rs index 64049584d8b..b7cf5c52f32 100644 --- a/radix-engine-tests/tests/kernel/panics.rs +++ b/radix-engine-tests/tests/kernel/panics.rs @@ -44,6 +44,11 @@ impl KernelApi for MockKernel { impl KernelThreadApi for MockKernel { type CallFrameData = Actor; + + fn kernel_switch_thread(&mut self, _id: usize) -> Result<(), RuntimeError> { + panic1!() + } + fn kernel_set_call_frame_data(&mut self, _data: Actor) -> Result<(), RuntimeError> { panic1!() } diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index f064353e333..04d0a43275f 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -50,6 +50,8 @@ mod royalty; mod royalty_auth; mod royalty_edge_cases; mod schema_sanity_check; + +mod subintents; mod system; mod system_access_rule; mod system_actor_collection; diff --git a/radix-engine/src/blueprints/transaction_processor/instructions.rs b/radix-engine/src/blueprints/transaction_processor/instructions.rs index c9d7fb662c6..f365228dd22 100644 --- a/radix-engine/src/blueprints/transaction_processor/instructions.rs +++ b/radix-engine/src/blueprints/transaction_processor/instructions.rs @@ -14,7 +14,7 @@ use radix_native_sdk::runtime::LocalAuthZone; use radix_rust::prelude::*; use radix_transactions::data::transform; use radix_transactions::model::manifest_instruction::*; -use radix_transactions::model::InstructionV1; +use radix_transactions::model::{InstructionV1, InstructionV2}; pub trait TxnInstruction { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( @@ -71,6 +71,55 @@ impl TxnInstruction for InstructionV1 { } } +impl TxnInstruction for InstructionV2 { + fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( + self, + worktop: &mut Worktop, + objects: &mut TxnProcessorObjects, + api: &mut Y, + ) -> Result { + match self { + InstructionV2::TakeAllFromWorktop(i) => i.execute(worktop, objects, api), + InstructionV2::TakeFromWorktop(i) => i.execute(worktop, objects, api), + InstructionV2::TakeNonFungiblesFromWorktop(i) => i.execute(worktop, objects, api), + InstructionV2::ReturnToWorktop(i) => i.execute(worktop, objects, api), + InstructionV2::AssertWorktopContainsAny(i) => i.execute(worktop, objects, api), + InstructionV2::AssertWorktopContains(i) => i.execute(worktop, objects, api), + InstructionV2::AssertWorktopContainsNonFungibles(i) => i.execute(worktop, objects, api), + InstructionV2::PopFromAuthZone(i) => i.execute(worktop, objects, api), + InstructionV2::PushToAuthZone(i) => i.execute(worktop, objects, api), + InstructionV2::CreateProofFromAuthZoneOfAmount(i) => i.execute(worktop, objects, api), + InstructionV2::CreateProofFromAuthZoneOfNonFungibles(i) => { + i.execute(worktop, objects, api) + } + InstructionV2::CreateProofFromAuthZoneOfAll(i) => i.execute(worktop, objects, api), + InstructionV2::CreateProofFromBucketOfAmount(i) => i.execute(worktop, objects, api), + InstructionV2::CreateProofFromBucketOfNonFungibles(i) => { + i.execute(worktop, objects, api) + } + InstructionV2::CreateProofFromBucketOfAll(i) => i.execute(worktop, objects, api), + InstructionV2::DropAuthZoneProofs(i) => i.execute(worktop, objects, api), + InstructionV2::DropAuthZoneRegularProofs(i) => i.execute(worktop, objects, api), + InstructionV2::DropAuthZoneSignatureProofs(i) => i.execute(worktop, objects, api), + InstructionV2::BurnResource(i) => i.execute(worktop, objects, api), + InstructionV2::CloneProof(i) => i.execute(worktop, objects, api), + InstructionV2::DropProof(i) => i.execute(worktop, objects, api), + InstructionV2::CallFunction(i) => i.execute(worktop, objects, api), + InstructionV2::CallMethod(i) => i.execute(worktop, objects, api), + InstructionV2::CallRoyaltyMethod(i) => i.execute(worktop, objects, api), + InstructionV2::CallMetadataMethod(i) => i.execute(worktop, objects, api), + InstructionV2::CallRoleAssignmentMethod(i) => i.execute(worktop, objects, api), + InstructionV2::CallDirectVaultMethod(i) => i.execute(worktop, objects, api), + InstructionV2::DropNamedProofs(i) => i.execute(worktop, objects, api), + InstructionV2::DropAllProofs(i) => i.execute(worktop, objects, api), + InstructionV2::AllocateGlobalAddress(i) => i.execute(worktop, objects, api), + InstructionV2::YieldToChild(i) => todo!(), + InstructionV2::YieldToParent(i) => todo!(), + InstructionV2::AuthenticateParent(_) => todo!(), + } + } +} + impl TxnInstruction for TakeAllFromWorktop { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, diff --git a/radix-engine/src/kernel/kernel.rs b/radix-engine/src/kernel/kernel.rs index 6852367ffe2..d7fa8262446 100644 --- a/radix-engine/src/kernel/kernel.rs +++ b/radix-engine/src/kernel/kernel.rs @@ -1186,6 +1186,12 @@ where impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelThreadApi for Kernel<'g, M, S> { type CallFrameData = M::CallFrameData; + + fn kernel_switch_thread(&mut self, id: usize) -> Result<(), RuntimeError> { + self.stacks.switch(id); + Ok(()) + } + fn kernel_set_call_frame_data(&mut self, data: M::CallFrameData) -> Result<(), RuntimeError> { *self.stacks.cur_mut().data_mut() = data; Ok(()) diff --git a/radix-engine/src/kernel/kernel_api.rs b/radix-engine/src/kernel/kernel_api.rs index 6d7af225238..9ac208cb891 100644 --- a/radix-engine/src/kernel/kernel_api.rs +++ b/radix-engine/src/kernel/kernel_api.rs @@ -174,6 +174,11 @@ pub trait KernelInvokeApi { /// API for managing threads and their associated call frame stack pub trait KernelThreadApi { type CallFrameData; + + /// Context switches to a different thread + fn kernel_switch_thread(&mut self, id: usize) -> Result<(), RuntimeError>; + + /// Sets the call frame data for the current thread's call frame fn kernel_set_call_frame_data(&mut self, data: Self::CallFrameData) -> Result<(), RuntimeError>; diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index 3908b7e04fa..c733035681e 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -135,7 +135,9 @@ impl VersionedSystemLogic { } VersionedSystemLogic::V2 => { let intents = executable.intents(); - for intent in intents { + for (thread_id, intent) in intents.iter().enumerate() { + api.kernel_switch_thread(thread_id)?; + let mut system_service = SystemService::new(api); let virtual_resources = intent .auth_zone_init @@ -161,10 +163,12 @@ impl VersionedSystemLogic { } { + api.kernel_switch_thread(0)?; + let mut system_service = SystemService::new(api); let intent = intents.get(0).unwrap(); - let txn_processor = TxnProcessor::::init( + let txn_processor = TxnProcessor::::init( intent.encoded_instructions.clone(), global_address_reservations.clone(), intent.blobs.clone(), diff --git a/radix-transactions/src/model/v2/child_intents_v2.rs b/radix-transactions/src/model/v2/child_intents_v2.rs index 44a33a69475..a3be772721b 100644 --- a/radix-transactions/src/model/v2/child_intents_v2.rs +++ b/radix-transactions/src/model/v2/child_intents_v2.rs @@ -14,7 +14,7 @@ impl TransactionPartialPrepare for ChildIntentsV2 { /// The first few of these will be the children of the given intent. #[derive(Debug, Clone, Copy, Eq, PartialEq, ManifestSbor, ScryptoDescribe)] #[sbor(transparent)] -pub struct ManifestIntent(u32); +pub struct ManifestIntent(pub u32); #[derive(Debug, Clone, Eq, PartialEq)] pub struct PreparedChildIntentsV2 { diff --git a/scrypto-test/src/ledger_simulator/inject_costing_err.rs b/scrypto-test/src/ledger_simulator/inject_costing_err.rs index 6f1d88acee7..032c92bc642 100644 --- a/scrypto-test/src/ledger_simulator/inject_costing_err.rs +++ b/scrypto-test/src/ledger_simulator/inject_costing_err.rs @@ -496,6 +496,11 @@ impl<'a, M: SystemCallbackObject, K: KernelApi { type CallFrameData = Actor; + + fn kernel_switch_thread(&mut self, id: usize) -> Result<(), RuntimeError> { + self.api.kernel_switch_thread(id) + } + fn kernel_set_call_frame_data(&mut self, data: Actor) -> Result<(), RuntimeError> { self.api.kernel_set_call_frame_data(data) } From cccce3a974611cfc86ba05531792b267cabbe63d Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Mon, 9 Sep 2024 17:21:11 -0500 Subject: [PATCH 03/12] Add txn thread execution --- .../transaction_processor/blueprint.rs | 5 ++-- .../transaction_processor/tx_processor.rs | 19 +++++++------ radix-engine/src/system/mod.rs | 1 + radix-engine/src/system/system_callback.rs | 28 +++++++++++++------ radix-engine/src/system/txn_threads.rs | 23 +++++++++++++++ 5 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 radix-engine/src/system/txn_threads.rs diff --git a/radix-engine/src/blueprints/transaction_processor/blueprint.rs b/radix-engine/src/blueprints/transaction_processor/blueprint.rs index 2ec63e6ac2b..3c13d6af8b4 100644 --- a/radix-engine/src/blueprints/transaction_processor/blueprint.rs +++ b/radix-engine/src/blueprints/transaction_processor/blueprint.rs @@ -57,13 +57,14 @@ impl TransactionProcessorBlueprint { TransactionProcessorV1MinorVersion::Zero => usize::MAX, TransactionProcessorV1MinorVersion::One => MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, }; - let txn_processor = TxnProcessor::::init( + let mut txn_processor = TxnProcessor::::init( Rc::new(manifest_encoded_instructions), global_address_reservations, Rc::new(blobs), max_total_size_of_blobs, api, )?; - txn_processor.execute(api) + txn_processor.execute(api)?; + Ok(txn_processor.outputs) } } diff --git a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs b/radix-engine/src/blueprints/transaction_processor/tx_processor.rs index 778df05f9e7..e28deffdb9b 100644 --- a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs +++ b/radix-engine/src/blueprints/transaction_processor/tx_processor.rs @@ -43,10 +43,11 @@ impl From for RuntimeError { } pub struct TxnProcessor { - instructions: Vec, + instructions: VecDeque, worktop: Worktop, objects: TxnProcessorObjects, - outputs: Vec, + pub instruction_index: usize, + pub outputs: Vec, } impl TxnProcessor { @@ -94,7 +95,8 @@ impl TxnProcessor { let outputs = Vec::new(); Ok(Self { - instructions, + instructions: instructions.into_iter().collect(), + instruction_index: 0usize, worktop, objects, outputs, @@ -105,18 +107,19 @@ impl TxnProcessor { Y: SystemApi + KernelNodeApi + KernelSubstateApi, L: Default, >( - mut self, + &mut self, api: &mut Y, - ) -> Result, RuntimeError> { - for (index, instruction) in self.instructions.into_iter().enumerate() { - api.update_instruction_index(index)?; + ) -> Result<(), RuntimeError> { + while let Some(instruction) = self.instructions.pop_front() { + api.update_instruction_index(self.instruction_index)?; let result = instruction.execute(&mut self.worktop, &mut self.objects, api)?; self.outputs.push(result); + self.instruction_index += 1; } self.worktop.drop(api)?; - Ok(self.outputs) + Ok(()) } } diff --git a/radix-engine/src/system/mod.rs b/radix-engine/src/system/mod.rs index 485a114cf26..8e398ba2acb 100644 --- a/radix-engine/src/system/mod.rs +++ b/radix-engine/src/system/mod.rs @@ -15,3 +15,4 @@ pub mod system_substate_schemas; pub mod system_substates; pub mod system_type_checker; pub mod type_info; +pub mod txn_threads; diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index c733035681e..c182782fd99 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -48,6 +48,7 @@ use radix_engine_interface::blueprints::package::*; use radix_engine_interface::blueprints::transaction_processor::*; use radix_substate_store_interface::{db_key_mapper::SpreadPrefixKeyMapper, interface::*}; use radix_transactions::model::*; +use crate::system::txn_threads::TxnThreads; pub const BOOT_LOADER_SYSTEM_SUBSTATE_FIELD_KEY: FieldKey = 1u8; @@ -134,6 +135,9 @@ impl VersionedSystemLogic { output } VersionedSystemLogic::V2 => { + let mut txn_processors = vec![]; + + // Setup let intents = executable.intents(); for (thread_id, intent) in intents.iter().enumerate() { api.kernel_switch_thread(thread_id)?; @@ -160,14 +164,8 @@ impl VersionedSystemLogic { ident: TRANSACTION_PROCESSOR_RUN_IDENT.to_string(), auth_zone, }))?; - } - - { - api.kernel_switch_thread(0)?; let mut system_service = SystemService::new(api); - let intent = intents.get(0).unwrap(); - let txn_processor = TxnProcessor::::init( intent.encoded_instructions.clone(), global_address_reservations.clone(), @@ -175,8 +173,20 @@ impl VersionedSystemLogic { MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, &mut system_service, )?; - let output = txn_processor.execute(&mut system_service)?; + txn_processors.push(txn_processor); + } + // Execution + let output = { + let mut txn_threads = TxnThreads { + threads: txn_processors, + }; + txn_threads.execute(api)?; + txn_threads.threads.remove(0).outputs + }; + + // Cleanup + { let owned_nodes = api.kernel_get_owned_nodes()?; System::auto_drop(owned_nodes, api)?; @@ -198,9 +208,9 @@ impl VersionedSystemLogic { owned_nodes, ))); } - - output } + + output } }; diff --git a/radix-engine/src/system/txn_threads.rs b/radix-engine/src/system/txn_threads.rs new file mode 100644 index 00000000000..8abe11b3a3d --- /dev/null +++ b/radix-engine/src/system/txn_threads.rs @@ -0,0 +1,23 @@ +use radix_engine_interface::blueprints::transaction_processor::InstructionOutput; +use radix_transactions::model::InstructionV2; +use crate::blueprints::transaction_processor::{MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, TxnProcessor}; +use crate::errors::RuntimeError; +use crate::system::system::SystemService; +use crate::system::system_callback::SystemBasedKernelApi; + +pub struct TxnThreads { + pub threads: Vec>, +} + +impl TxnThreads { + pub fn execute(&mut self, api: &mut Y) -> Result<(), RuntimeError> { + api.kernel_switch_thread(0)?; + + let mut system_service = SystemService::new(api); + let mut txn_processor = self.threads.get_mut(0).unwrap(); + + txn_processor.execute(&mut system_service)?; + + Ok(()) + } +} \ No newline at end of file From 0775e3b03b9053a584d251a74d2186114df4bb26 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Mon, 9 Sep 2024 18:28:02 -0500 Subject: [PATCH 04/12] Add simple subintent transaction test --- radix-engine-tests/tests/system/subintents.rs | 39 ++++++ .../transaction_processor/blueprint.rs | 9 +- .../transaction_processor/instructions.rs | 102 ++++++++------ .../transaction_processor/tx_processor.rs | 23 +-- radix-engine/src/system/mod.rs | 2 +- radix-engine/src/system/system_callback.rs | 91 ++---------- radix-engine/src/system/txn_threads.rs | 132 ++++++++++++++++-- 7 files changed, 255 insertions(+), 143 deletions(-) create mode 100644 radix-engine-tests/tests/system/subintents.rs diff --git a/radix-engine-tests/tests/system/subintents.rs b/radix-engine-tests/tests/system/subintents.rs new file mode 100644 index 00000000000..47539a16adb --- /dev/null +++ b/radix-engine-tests/tests/system/subintents.rs @@ -0,0 +1,39 @@ +use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId}; +use radix_engine::transaction::ExecutionConfig; +use radix_rust::btreeset; +use radix_transactions::builder::ManifestV2Builder; +use radix_transactions::model::{ManifestIntent, TestTransaction}; +use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; + +#[test] +fn simple_subintent_transaction_should_work() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + + // Act + let intents = vec![ + { + let manifest = ManifestV2Builder::new_v2() + .lock_standard_test_fee(account) + .yield_to_child(ManifestIntent(1), ()) + .build(); + (manifest, ledger.next_transaction_nonce()) + }, + { + let manifest = ManifestV2Builder::new_v2().build(); + (manifest, ledger.next_transaction_nonce()) + }, + ]; + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_commit_success(); +} diff --git a/radix-engine/src/blueprints/transaction_processor/blueprint.rs b/radix-engine/src/blueprints/transaction_processor/blueprint.rs index 3c13d6af8b4..2345d2dcc8a 100644 --- a/radix-engine/src/blueprints/transaction_processor/blueprint.rs +++ b/radix-engine/src/blueprints/transaction_processor/blueprint.rs @@ -1,4 +1,4 @@ -use crate::blueprints::transaction_processor::TxnProcessor; +use crate::blueprints::transaction_processor::TxnProcessorThread; use crate::errors::RuntimeError; use crate::internal_prelude::{Sbor, ScryptoEncode, ScryptoSbor}; use crate::kernel::kernel_api::{KernelNodeApi, KernelSubstateApi}; @@ -57,14 +57,17 @@ impl TransactionProcessorBlueprint { TransactionProcessorV1MinorVersion::Zero => usize::MAX, TransactionProcessorV1MinorVersion::One => MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, }; - let mut txn_processor = TxnProcessor::::init( + let mut txn_processor = TxnProcessorThread::::init( Rc::new(manifest_encoded_instructions), global_address_reservations, Rc::new(blobs), max_total_size_of_blobs, api, )?; - txn_processor.execute(api)?; + let yield_inst = txn_processor.resume(api)?; + if yield_inst.is_some() { + panic!("This should never yield"); + } Ok(txn_processor.outputs) } } diff --git a/radix-engine/src/blueprints/transaction_processor/instructions.rs b/radix-engine/src/blueprints/transaction_processor/instructions.rs index f365228dd22..821ea76a855 100644 --- a/radix-engine/src/blueprints/transaction_processor/instructions.rs +++ b/radix-engine/src/blueprints/transaction_processor/instructions.rs @@ -16,13 +16,18 @@ use radix_transactions::data::transform; use radix_transactions::model::manifest_instruction::*; use radix_transactions::model::{InstructionV1, InstructionV2}; +pub enum Yield { + ToChild(usize), + ToParent, +} + pub trait TxnInstruction { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, objects: &mut TxnProcessorObjects, api: &mut Y, - ) -> Result; + ) -> Result<(InstructionOutput, Option), RuntimeError>; } impl TxnInstruction for InstructionV1 { @@ -31,8 +36,8 @@ impl TxnInstruction for InstructionV1 { worktop: &mut Worktop, objects: &mut TxnProcessorObjects, api: &mut Y, - ) -> Result { - match self { + ) -> Result<(InstructionOutput, Option), RuntimeError> { + let output = match self { InstructionV1::TakeAllFromWorktop(i) => i.execute(worktop, objects, api), InstructionV1::TakeFromWorktop(i) => i.execute(worktop, objects, api), InstructionV1::TakeNonFungiblesFromWorktop(i) => i.execute(worktop, objects, api), @@ -67,7 +72,8 @@ impl TxnInstruction for InstructionV1 { InstructionV1::DropNamedProofs(i) => i.execute(worktop, objects, api), InstructionV1::DropAllProofs(i) => i.execute(worktop, objects, api), InstructionV1::AllocateGlobalAddress(i) => i.execute(worktop, objects, api), - } + }?; + Ok((output, None)) } } @@ -77,8 +83,8 @@ impl TxnInstruction for InstructionV2 { worktop: &mut Worktop, objects: &mut TxnProcessorObjects, api: &mut Y, - ) -> Result { - match self { + ) -> Result<(InstructionOutput, Option), RuntimeError> { + let output = match self { InstructionV2::TakeAllFromWorktop(i) => i.execute(worktop, objects, api), InstructionV2::TakeFromWorktop(i) => i.execute(worktop, objects, api), InstructionV2::TakeNonFungiblesFromWorktop(i) => i.execute(worktop, objects, api), @@ -113,14 +119,32 @@ impl TxnInstruction for InstructionV2 { InstructionV2::DropNamedProofs(i) => i.execute(worktop, objects, api), InstructionV2::DropAllProofs(i) => i.execute(worktop, objects, api), InstructionV2::AllocateGlobalAddress(i) => i.execute(worktop, objects, api), - InstructionV2::YieldToChild(i) => todo!(), - InstructionV2::YieldToParent(i) => todo!(), + InstructionV2::YieldToChild(i) => { + return Ok(( + InstructionOutput::None, + Some(Yield::ToChild(i.child_index.0 as usize)), + )); + } + InstructionV2::YieldToParent(_) => { + return Ok((InstructionOutput::None, Some(Yield::ToParent))); + } InstructionV2::AuthenticateParent(_) => todo!(), - } + }?; + + Ok((output, None)) } } -impl TxnInstruction for TakeAllFromWorktop { +pub trait TxnNormalInstruction { + fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( + self, + worktop: &mut Worktop, + objects: &mut TxnProcessorObjects, + api: &mut Y, + ) -> Result; +} + +impl TxnNormalInstruction for TakeAllFromWorktop { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -133,7 +157,7 @@ impl TxnInstruction for TakeAllFromWorktop { } } -impl TxnInstruction for TakeFromWorktop { +impl TxnNormalInstruction for TakeFromWorktop { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -146,7 +170,7 @@ impl TxnInstruction for TakeFromWorktop { } } -impl TxnInstruction for TakeNonFungiblesFromWorktop { +impl TxnNormalInstruction for TakeNonFungiblesFromWorktop { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -163,7 +187,7 @@ impl TxnInstruction for TakeNonFungiblesFromWorktop { } } -impl TxnInstruction for ReturnToWorktop { +impl TxnNormalInstruction for ReturnToWorktop { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -176,7 +200,7 @@ impl TxnInstruction for ReturnToWorktop { } } -impl TxnInstruction for AssertWorktopContainsAny { +impl TxnNormalInstruction for AssertWorktopContainsAny { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -188,7 +212,7 @@ impl TxnInstruction for AssertWorktopContainsAny { } } -impl TxnInstruction for AssertWorktopContains { +impl TxnNormalInstruction for AssertWorktopContains { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -200,7 +224,7 @@ impl TxnInstruction for AssertWorktopContains { } } -impl TxnInstruction for AssertWorktopContainsNonFungibles { +impl TxnNormalInstruction for AssertWorktopContainsNonFungibles { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -216,7 +240,7 @@ impl TxnInstruction for AssertWorktopContainsNonFungibles { } } -impl TxnInstruction for PopFromAuthZone { +impl TxnNormalInstruction for PopFromAuthZone { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -231,7 +255,7 @@ impl TxnInstruction for PopFromAuthZone { } } -impl TxnInstruction for PushToAuthZone { +impl TxnNormalInstruction for PushToAuthZone { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -244,7 +268,7 @@ impl TxnInstruction for PushToAuthZone { } } -impl TxnInstruction for CreateProofFromAuthZoneOfAmount { +impl TxnNormalInstruction for CreateProofFromAuthZoneOfAmount { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -257,7 +281,7 @@ impl TxnInstruction for CreateProofFromAuthZoneOfAmount { } } -impl TxnInstruction for CreateProofFromAuthZoneOfNonFungibles { +impl TxnNormalInstruction for CreateProofFromAuthZoneOfNonFungibles { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -274,7 +298,7 @@ impl TxnInstruction for CreateProofFromAuthZoneOfNonFungibles { } } -impl TxnInstruction for CreateProofFromAuthZoneOfAll { +impl TxnNormalInstruction for CreateProofFromAuthZoneOfAll { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -287,7 +311,7 @@ impl TxnInstruction for CreateProofFromAuthZoneOfAll { } } -impl TxnInstruction for CreateProofFromBucketOfAmount { +impl TxnNormalInstruction for CreateProofFromBucketOfAmount { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -301,7 +325,7 @@ impl TxnInstruction for CreateProofFromBucketOfAmount { } } -impl TxnInstruction for CreateProofFromBucketOfNonFungibles { +impl TxnNormalInstruction for CreateProofFromBucketOfNonFungibles { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -315,7 +339,7 @@ impl TxnInstruction for CreateProofFromBucketOfNonFungibles { } } -impl TxnInstruction for CreateProofFromBucketOfAll { +impl TxnNormalInstruction for CreateProofFromBucketOfAll { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -329,7 +353,7 @@ impl TxnInstruction for CreateProofFromBucketOfAll { } } -impl TxnInstruction for DropAuthZoneProofs { +impl TxnNormalInstruction for DropAuthZoneProofs { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -341,7 +365,7 @@ impl TxnInstruction for DropAuthZoneProofs { } } -impl TxnInstruction for DropAuthZoneRegularProofs { +impl TxnNormalInstruction for DropAuthZoneRegularProofs { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -353,7 +377,7 @@ impl TxnInstruction for DropAuthZoneRegularProofs { } } -impl TxnInstruction for DropAuthZoneSignatureProofs { +impl TxnNormalInstruction for DropAuthZoneSignatureProofs { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -365,7 +389,7 @@ impl TxnInstruction for DropAuthZoneSignatureProofs { } } -impl TxnInstruction for BurnResource { +impl TxnNormalInstruction for BurnResource { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -381,7 +405,7 @@ impl TxnInstruction for BurnResource { } } -impl TxnInstruction for CloneProof { +impl TxnNormalInstruction for CloneProof { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -395,7 +419,7 @@ impl TxnInstruction for CloneProof { } } -impl TxnInstruction for DropProof { +impl TxnNormalInstruction for DropProof { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -433,7 +457,7 @@ fn handle_invocation + KernelSubstateApi, L: Defau Ok(InstructionOutput::CallReturn(result.into())) } -impl TxnInstruction for CallFunction { +impl TxnNormalInstruction for CallFunction { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -452,7 +476,7 @@ impl TxnInstruction for CallFunction { } } -impl TxnInstruction for CallMethod { +impl TxnNormalInstruction for CallMethod { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -470,7 +494,7 @@ impl TxnInstruction for CallMethod { } } -impl TxnInstruction for CallRoyaltyMethod { +impl TxnNormalInstruction for CallRoyaltyMethod { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -489,7 +513,7 @@ impl TxnInstruction for CallRoyaltyMethod { } } -impl TxnInstruction for CallMetadataMethod { +impl TxnNormalInstruction for CallMetadataMethod { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -508,7 +532,7 @@ impl TxnInstruction for CallMetadataMethod { } } -impl TxnInstruction for CallRoleAssignmentMethod { +impl TxnNormalInstruction for CallRoleAssignmentMethod { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -527,7 +551,7 @@ impl TxnInstruction for CallRoleAssignmentMethod { } } -impl TxnInstruction for CallDirectVaultMethod { +impl TxnNormalInstruction for CallDirectVaultMethod { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, worktop: &mut Worktop, @@ -544,7 +568,7 @@ impl TxnInstruction for CallDirectVaultMethod { } } -impl TxnInstruction for DropNamedProofs { +impl TxnNormalInstruction for DropNamedProofs { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -559,7 +583,7 @@ impl TxnInstruction for DropNamedProofs { } } -impl TxnInstruction for DropAllProofs { +impl TxnNormalInstruction for DropAllProofs { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, @@ -575,7 +599,7 @@ impl TxnInstruction for DropAllProofs { } } -impl TxnInstruction for AllocateGlobalAddress { +impl TxnNormalInstruction for AllocateGlobalAddress { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, _worktop: &mut Worktop, diff --git a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs b/radix-engine/src/blueprints/transaction_processor/tx_processor.rs index e28deffdb9b..f231e116c9e 100644 --- a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs +++ b/radix-engine/src/blueprints/transaction_processor/tx_processor.rs @@ -1,5 +1,5 @@ use crate::blueprints::resource::WorktopSubstate; -use crate::blueprints::transaction_processor::TxnInstruction; +use crate::blueprints::transaction_processor::{TxnInstruction, Yield}; use crate::errors::ApplicationError; use crate::errors::RuntimeError; use crate::internal_prelude::*; @@ -42,7 +42,7 @@ impl From for RuntimeError { } } -pub struct TxnProcessor { +pub struct TxnProcessorThread { instructions: VecDeque, worktop: Worktop, objects: TxnProcessorObjects, @@ -50,7 +50,7 @@ pub struct TxnProcessor pub outputs: Vec, } -impl TxnProcessor { +impl TxnProcessorThread { pub fn init + KernelNodeApi + KernelSubstateApi, L: Default>( manifest_encoded_instructions: Rc>, global_address_reservations: Vec, @@ -103,23 +103,24 @@ impl TxnProcessor { }) } - pub fn execute< - Y: SystemApi + KernelNodeApi + KernelSubstateApi, - L: Default, - >( + pub fn resume + KernelNodeApi + KernelSubstateApi, L: Default>( &mut self, api: &mut Y, - ) -> Result<(), RuntimeError> { + ) -> Result, RuntimeError> { while let Some(instruction) = self.instructions.pop_front() { api.update_instruction_index(self.instruction_index)?; - let result = instruction.execute(&mut self.worktop, &mut self.objects, api)?; - self.outputs.push(result); + let (output, yield_instruction) = + instruction.execute(&mut self.worktop, &mut self.objects, api)?; + self.outputs.push(output); self.instruction_index += 1; + if yield_instruction.is_some() { + return Ok(yield_instruction); + } } self.worktop.drop(api)?; - Ok(()) + Ok(None) } } diff --git a/radix-engine/src/system/mod.rs b/radix-engine/src/system/mod.rs index 8e398ba2acb..53321422697 100644 --- a/radix-engine/src/system/mod.rs +++ b/radix-engine/src/system/mod.rs @@ -14,5 +14,5 @@ pub mod system_modules; pub mod system_substate_schemas; pub mod system_substates; pub mod system_type_checker; -pub mod type_info; pub mod txn_threads; +pub mod type_info; diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index c182782fd99..72e767ef35b 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -9,8 +9,7 @@ use crate::blueprints::identity::IDENTITY_CREATE_PREALLOCATED_SECP256K1_ID; use crate::blueprints::resource::fungible_vault::{DepositEvent, PayFeeEvent}; use crate::blueprints::resource::*; use crate::blueprints::transaction_processor::{ - TransactionProcessorRunInputEfficientEncodable, TxnProcessor, - MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, + TransactionProcessorRunInputEfficientEncodable, }; use crate::blueprints::transaction_tracker::*; use crate::errors::*; @@ -35,6 +34,7 @@ use crate::system::system_modules::transaction_runtime::TransactionRuntimeModule use crate::system::system_modules::{EnabledModules, SystemModuleMixer}; use crate::system::system_substates::KeyValueEntrySubstate; use crate::system::system_type_checker::{BlueprintTypeTarget, KVStoreTypeTarget}; +use crate::system::txn_threads::TxnThreads; use crate::track::*; use crate::transaction::*; use radix_blueprint_schema_init::RefTypes; @@ -48,7 +48,6 @@ use radix_engine_interface::blueprints::package::*; use radix_engine_interface::blueprints::transaction_processor::*; use radix_substate_store_interface::{db_key_mapper::SpreadPrefixKeyMapper, interface::*}; use radix_transactions::model::*; -use crate::system::txn_threads::TxnThreads; pub const BOOT_LOADER_SYSTEM_SUBSTATE_FIELD_KEY: FieldKey = 1u8; @@ -135,81 +134,17 @@ impl VersionedSystemLogic { output } VersionedSystemLogic::V2 => { - let mut txn_processors = vec![]; - - // Setup - let intents = executable.intents(); - for (thread_id, intent) in intents.iter().enumerate() { - api.kernel_switch_thread(thread_id)?; - - let mut system_service = SystemService::new(api); - let virtual_resources = intent - .auth_zone_init - .simulate_every_proof_under_resources - .clone(); - let virtual_non_fungibles = - intent.auth_zone_init.initial_non_fungible_id_proofs.clone(); - let auth_zone = AuthModule::create_auth_zone( - &mut system_service, - None, - virtual_resources, - virtual_non_fungibles, - )?; - - api.kernel_set_call_frame_data(Actor::Function(FunctionActor { - blueprint_id: BlueprintId::new( - &TRANSACTION_PROCESSOR_PACKAGE, - TRANSACTION_PROCESSOR_BLUEPRINT, - ), - ident: TRANSACTION_PROCESSOR_RUN_IDENT.to_string(), - auth_zone, - }))?; - - let mut system_service = SystemService::new(api); - let txn_processor = TxnProcessor::::init( - intent.encoded_instructions.clone(), - global_address_reservations.clone(), - intent.blobs.clone(), - MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, - &mut system_service, - )?; - txn_processors.push(txn_processor); - } - - // Execution - let output = { - let mut txn_threads = TxnThreads { - threads: txn_processors, - }; - txn_threads.execute(api)?; - txn_threads.threads.remove(0).outputs - }; - - // Cleanup - { - let owned_nodes = api.kernel_get_owned_nodes()?; - System::auto_drop(owned_nodes, api)?; - - let actor = api.kernel_get_system_state().current_call_frame; - match actor { - Actor::Function(FunctionActor { auth_zone, .. }) => { - let auth_zone = auth_zone.clone(); - let mut system_service = SystemService::new(api); - AuthModule::teardown_auth_zone(&mut system_service, auth_zone)?; - } - _ => { - panic!("unexpected"); - } - } - - let owned_nodes = api.kernel_get_owned_nodes()?; - if !owned_nodes.is_empty() { - return Err(RuntimeError::KernelError(KernelError::OrphanedNodes( - owned_nodes, - ))); - } - } - + let mut txn_threads = + TxnThreads::init(executable, global_address_reservations, api)?; + txn_threads.execute(api)?; + let output = txn_threads + .threads + .get_mut(0) + .unwrap() + .outputs + .drain(..) + .collect(); + txn_threads.cleanup(api)?; output } }; diff --git a/radix-engine/src/system/txn_threads.rs b/radix-engine/src/system/txn_threads.rs index 8abe11b3a3d..7c789580c04 100644 --- a/radix-engine/src/system/txn_threads.rs +++ b/radix-engine/src/system/txn_threads.rs @@ -1,23 +1,133 @@ -use radix_engine_interface::blueprints::transaction_processor::InstructionOutput; -use radix_transactions::model::InstructionV2; -use crate::blueprints::transaction_processor::{MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, TxnProcessor}; -use crate::errors::RuntimeError; +use crate::blueprints::transaction_processor::{ + TxnProcessorThread, Yield, MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, +}; +use crate::errors::{KernelError, RuntimeError}; +use crate::kernel::kernel_callback_api::KernelCallbackObject; +use crate::system::actor::{Actor, FunctionActor}; use crate::system::system::SystemService; -use crate::system::system_callback::SystemBasedKernelApi; +use crate::system::system_callback::{System, SystemBasedKernelApi}; +use crate::system::system_modules::auth::AuthModule; +use radix_common::constants::TRANSACTION_PROCESSOR_PACKAGE; +use radix_common::prelude::{BlueprintId, GlobalAddressReservation}; +use radix_engine_interface::blueprints::transaction_processor::{ + TRANSACTION_PROCESSOR_BLUEPRINT, TRANSACTION_PROCESSOR_RUN_IDENT, +}; +use radix_transactions::model::{ExecutableTransaction, InstructionV2}; pub struct TxnThreads { - pub threads: Vec>, + pub threads: Vec>, } impl TxnThreads { + pub fn init( + executable: ExecutableTransaction, + global_address_reservations: Vec, + api: &mut Y, + ) -> Result { + let mut txn_processors = vec![]; + + // Setup + let intents = executable.intents(); + for (thread_id, intent) in intents.iter().enumerate() { + api.kernel_switch_thread(thread_id)?; + + let mut system_service = SystemService::new(api); + let virtual_resources = intent + .auth_zone_init + .simulate_every_proof_under_resources + .clone(); + let virtual_non_fungibles = + intent.auth_zone_init.initial_non_fungible_id_proofs.clone(); + let auth_zone = AuthModule::create_auth_zone( + &mut system_service, + None, + virtual_resources, + virtual_non_fungibles, + )?; + + api.kernel_set_call_frame_data(Actor::Function(FunctionActor { + blueprint_id: BlueprintId::new( + &TRANSACTION_PROCESSOR_PACKAGE, + TRANSACTION_PROCESSOR_BLUEPRINT, + ), + ident: TRANSACTION_PROCESSOR_RUN_IDENT.to_string(), + auth_zone, + }))?; + + let mut system_service = SystemService::new(api); + let txn_processor = TxnProcessorThread::::init( + intent.encoded_instructions.clone(), + global_address_reservations.clone(), + intent.blobs.clone(), + MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, + &mut system_service, + )?; + txn_processors.push(txn_processor); + } + Ok(Self { + threads: txn_processors, + }) + } + pub fn execute(&mut self, api: &mut Y) -> Result<(), RuntimeError> { - api.kernel_switch_thread(0)?; + let mut cur_thread = 0; + let mut parent_stack = vec![]; - let mut system_service = SystemService::new(api); - let mut txn_processor = self.threads.get_mut(0).unwrap(); + loop { + api.kernel_switch_thread(cur_thread)?; + let txn_thread = self.threads.get_mut(cur_thread).unwrap(); - txn_processor.execute(&mut system_service)?; + let mut system_service = SystemService::new(api); + match txn_thread.resume(&mut system_service)? { + Some(Yield::ToChild(child)) => { + parent_stack.push(cur_thread); + cur_thread = child; + } + Some(Yield::ToParent) => { + cur_thread = parent_stack.pop().unwrap(); + } + None => { + if let Some(parent) = parent_stack.pop() { + cur_thread = parent; + } else { + break; + } + } + } + } + + assert!(parent_stack.is_empty()); Ok(()) } -} \ No newline at end of file + + pub fn cleanup(self, api: &mut Y) -> Result<(), RuntimeError> { + for (thread_id, _intent) in self.threads.iter().enumerate() { + api.kernel_switch_thread(thread_id)?; + + let owned_nodes = api.kernel_get_owned_nodes()?; + System::auto_drop(owned_nodes, api)?; + + let actor = api.kernel_get_system_state().current_call_frame; + match actor { + Actor::Function(FunctionActor { auth_zone, .. }) => { + let auth_zone = auth_zone.clone(); + let mut system_service = SystemService::new(api); + AuthModule::teardown_auth_zone(&mut system_service, auth_zone)?; + } + _ => { + panic!("unexpected"); + } + } + + let owned_nodes = api.kernel_get_owned_nodes()?; + if !owned_nodes.is_empty() { + return Err(RuntimeError::KernelError(KernelError::OrphanedNodes( + owned_nodes, + ))); + } + } + + Ok(()) + } +} From 64e74339513ded38a0ea4c64e9cc0024844b9647 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 16:13:24 -0500 Subject: [PATCH 05/12] Add children mapping --- .../tests/protocol/transaction.rs | 2 +- radix-engine-tests/tests/system/subintents.rs | 6 ++--- .../transaction_processor/instructions.rs | 3 +-- radix-engine/src/system/system_callback.rs | 5 ++--- radix-engine/src/system/txn_threads.rs | 8 ++++--- .../src/model/v1/test_transaction.rs | 22 ++++++++++++++----- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/radix-engine-tests/tests/protocol/transaction.rs b/radix-engine-tests/tests/protocol/transaction.rs index d5689806f3c..27875fcf2e7 100644 --- a/radix-engine-tests/tests/protocol/transaction.rs +++ b/radix-engine-tests/tests/protocol/transaction.rs @@ -19,7 +19,7 @@ fn bottlenose_protocol_should_not_support_v2_transactions() { let manifest = ManifestV2Builder::new_v2() .lock_standard_test_fee(account) .build(); - (manifest, ledger.next_transaction_nonce()) + (manifest, ledger.next_transaction_nonce(), vec![]) }) .collect(); let receipt = ledger.execute_transaction( diff --git a/radix-engine-tests/tests/system/subintents.rs b/radix-engine-tests/tests/system/subintents.rs index 47539a16adb..7bc496f65b4 100644 --- a/radix-engine-tests/tests/system/subintents.rs +++ b/radix-engine-tests/tests/system/subintents.rs @@ -16,13 +16,13 @@ fn simple_subintent_transaction_should_work() { { let manifest = ManifestV2Builder::new_v2() .lock_standard_test_fee(account) - .yield_to_child(ManifestIntent(1), ()) + .yield_to_child(ManifestIntent(0), ()) .build(); - (manifest, ledger.next_transaction_nonce()) + (manifest, ledger.next_transaction_nonce(), vec![1]) }, { let manifest = ManifestV2Builder::new_v2().build(); - (manifest, ledger.next_transaction_nonce()) + (manifest, ledger.next_transaction_nonce(), vec![]) }, ]; diff --git a/radix-engine/src/blueprints/transaction_processor/instructions.rs b/radix-engine/src/blueprints/transaction_processor/instructions.rs index 4245d26b419..6b46d6ef36c 100644 --- a/radix-engine/src/blueprints/transaction_processor/instructions.rs +++ b/radix-engine/src/blueprints/transaction_processor/instructions.rs @@ -13,8 +13,8 @@ use radix_native_sdk::resource::{ use radix_native_sdk::runtime::LocalAuthZone; use radix_rust::prelude::*; use radix_transactions::data::transform; -use radix_transactions::model::{InstructionV1, InstructionV2}; use radix_transactions::manifest::*; +use radix_transactions::model::{InstructionV1, InstructionV2}; use radix_transactions::prelude::*; pub enum Yield { @@ -22,7 +22,6 @@ pub enum Yield { ToParent, } - pub trait TxnInstruction { fn execute + KernelNodeApi + KernelSubstateApi, L: Default>( self, diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index 72e767ef35b..55ca67e6636 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -8,9 +8,7 @@ use crate::blueprints::identity::IDENTITY_CREATE_PREALLOCATED_ED25519_ID; use crate::blueprints::identity::IDENTITY_CREATE_PREALLOCATED_SECP256K1_ID; use crate::blueprints::resource::fungible_vault::{DepositEvent, PayFeeEvent}; use crate::blueprints::resource::*; -use crate::blueprints::transaction_processor::{ - TransactionProcessorRunInputEfficientEncodable, -}; +use crate::blueprints::transaction_processor::TransactionProcessorRunInputEfficientEncodable; use crate::blueprints::transaction_tracker::*; use crate::errors::*; use crate::internal_prelude::*; @@ -141,6 +139,7 @@ impl VersionedSystemLogic { .threads .get_mut(0) .unwrap() + .0 .outputs .drain(..) .collect(); diff --git a/radix-engine/src/system/txn_threads.rs b/radix-engine/src/system/txn_threads.rs index 7c789580c04..9881a37a56b 100644 --- a/radix-engine/src/system/txn_threads.rs +++ b/radix-engine/src/system/txn_threads.rs @@ -15,7 +15,7 @@ use radix_engine_interface::blueprints::transaction_processor::{ use radix_transactions::model::{ExecutableTransaction, InstructionV2}; pub struct TxnThreads { - pub threads: Vec>, + pub threads: Vec<(TxnProcessorThread, Vec)>, } impl TxnThreads { @@ -62,7 +62,8 @@ impl TxnThreads { MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, &mut system_service, )?; - txn_processors.push(txn_processor); + + txn_processors.push((txn_processor, intent.children_intent_indices.clone())); } Ok(Self { threads: txn_processors, @@ -75,11 +76,12 @@ impl TxnThreads { loop { api.kernel_switch_thread(cur_thread)?; - let txn_thread = self.threads.get_mut(cur_thread).unwrap(); + let (txn_thread, children_mapping) = self.threads.get_mut(cur_thread).unwrap(); let mut system_service = SystemService::new(api); match txn_thread.resume(&mut system_service)? { Some(Yield::ToChild(child)) => { + let child = *children_mapping.get(child).unwrap(); parent_stack.push(cur_thread); cur_thread = child; } diff --git a/radix-transactions/src/model/v1/test_transaction.rs b/radix-transactions/src/model/v1/test_transaction.rs index fe991b5e4b7..8ab1a24d18e 100644 --- a/radix-transactions/src/model/v1/test_transaction.rs +++ b/radix-transactions/src/model/v1/test_transaction.rs @@ -24,6 +24,7 @@ pub struct TestIntentV2 { pub instructions: InstructionsV2, pub blobs: BlobsV1, pub hash: Hash, + pub children_intent_indices: Vec, } #[derive(ManifestSbor)] @@ -38,6 +39,7 @@ pub struct PreparedTestIntent { pub references: IndexSet, pub blobs: Rc>>, pub hash: Hash, + pub children_intent_indices: Vec, } impl TestTransaction { @@ -55,23 +57,30 @@ impl TestTransaction { }) } - pub fn new_v2_from_nonce(intents: Vec<(TransactionManifestV2, u32)>) -> Self { + pub fn new_v2_from_nonce(intents: Vec<(TransactionManifestV2, u32, Vec)>) -> Self { let intents = intents .into_iter() - .map(|(manifest, nonce)| (manifest, hash(format!("Test transaction: {}", nonce)))) + .map(|(manifest, nonce, children_intent_indices)| { + ( + manifest, + hash(format!("Test transaction: {}", nonce)), + children_intent_indices, + ) + }) .collect(); Self::new_v2(intents) } - pub fn new_v2(intents: Vec<(TransactionManifestV2, Hash)>) -> Self { + pub fn new_v2(intents: Vec<(TransactionManifestV2, Hash, Vec)>) -> Self { let intents = intents .into_iter() - .map(|(manifest, hash)| { + .map(|(manifest, hash, children_intent_indices)| { let (instructions, blobs, ..) = manifest.for_intent(); TestIntentV2 { instructions, blobs, hash, + children_intent_indices, } }) .collect(); @@ -89,6 +98,7 @@ impl TestTransaction { references: prepared_instructions.references, blobs: intent.blobs.prepare_partial()?.blobs_by_hash, hash: intent.hash, + children_intent_indices: vec![], })) } Self::V2(intents) => { @@ -102,6 +112,7 @@ impl TestTransaction { references: prepared_instructions.references.deref().clone(), blobs: intent.blobs.prepare_partial()?.blobs_by_hash, hash: intent.hash, + children_intent_indices: intent.children_intent_indices, }); } @@ -174,6 +185,7 @@ impl PreparedTestTransaction { let intents = intents .iter() .map(|intent| { + // TODO let auth_zone_init = root_auth_zone .take() .unwrap_or(AuthZoneInit::proofs(Default::default())); @@ -183,7 +195,7 @@ impl PreparedTestTransaction { auth_zone_init, references: intent.references.clone(), blobs: intent.blobs.clone(), - children_intent_indices: vec![], + children_intent_indices: intent.children_intent_indices.clone(), } }) .collect(); From 72ebc91faf00f03a8c256f99b0c8f7e5d5e4b4a2 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 16:37:21 -0500 Subject: [PATCH 06/12] Add subintent transaction shape tests --- radix-engine-tests/tests/system/mod.rs | 2 +- .../tests/system/subintent_txn_shape.rs | 55 +++++++++++++++++++ radix-engine-tests/tests/system/subintents.rs | 39 ------------- 3 files changed, 56 insertions(+), 40 deletions(-) create mode 100644 radix-engine-tests/tests/system/subintent_txn_shape.rs delete mode 100644 radix-engine-tests/tests/system/subintents.rs diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index 04d0a43275f..caddfe4d30f 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -51,7 +51,7 @@ mod royalty_auth; mod royalty_edge_cases; mod schema_sanity_check; -mod subintents; +mod subintent_txn_shape; mod system; mod system_access_rule; mod system_actor_collection; diff --git a/radix-engine-tests/tests/system/subintent_txn_shape.rs b/radix-engine-tests/tests/system/subintent_txn_shape.rs new file mode 100644 index 00000000000..29aa08121d3 --- /dev/null +++ b/radix-engine-tests/tests/system/subintent_txn_shape.rs @@ -0,0 +1,55 @@ +use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId}; +use radix_engine::transaction::ExecutionConfig; +use radix_rust::btreeset; +use radix_transactions::builder::ManifestV2Builder; +use radix_transactions::model::{ManifestIntent, TestTransaction}; +use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; + +#[test] +fn simple_subintent_should_work() { + test_subintent_txn_shape(vec![vec![1], vec![]]); +} + +#[test] +fn multiple_flat_subintents_should_work() { + test_subintent_txn_shape(vec![vec![1, 2, 3, 4], vec![], vec![], vec![], vec![]]); +} + +#[test] +fn multiple_deep_subintents_should_work() { + test_subintent_txn_shape(vec![vec![1], vec![2], vec![3], vec![4], vec![]]); +} + +fn test_subintent_txn_shape(children: Vec>) { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + + // Act + let mut intents = vec![]; + + for (index, intent_children) in children.into_iter().enumerate() { + let mut builder = ManifestV2Builder::new_v2(); + if index == 0 { + builder = builder.lock_standard_test_fee(account); + } + for (index, _) in intent_children.iter().enumerate() { + builder = builder.yield_to_child(ManifestIntent(index as u32), ()); + } + + let manifest = builder.build(); + + intents.push((manifest, ledger.next_transaction_nonce(), intent_children)) + } + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_commit_success(); +} diff --git a/radix-engine-tests/tests/system/subintents.rs b/radix-engine-tests/tests/system/subintents.rs deleted file mode 100644 index 7bc496f65b4..00000000000 --- a/radix-engine-tests/tests/system/subintents.rs +++ /dev/null @@ -1,39 +0,0 @@ -use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId}; -use radix_engine::transaction::ExecutionConfig; -use radix_rust::btreeset; -use radix_transactions::builder::ManifestV2Builder; -use radix_transactions::model::{ManifestIntent, TestTransaction}; -use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; - -#[test] -fn simple_subintent_transaction_should_work() { - // Arrange - let mut ledger = LedgerSimulatorBuilder::new().build(); - let (public_key, _, account) = ledger.new_allocated_account(); - - // Act - let intents = vec![ - { - let manifest = ManifestV2Builder::new_v2() - .lock_standard_test_fee(account) - .yield_to_child(ManifestIntent(0), ()) - .build(); - (manifest, ledger.next_transaction_nonce(), vec![1]) - }, - { - let manifest = ManifestV2Builder::new_v2().build(); - (manifest, ledger.next_transaction_nonce(), vec![]) - }, - ]; - - let receipt = ledger.execute_transaction( - TestTransaction::new_v2_from_nonce(intents) - .prepare() - .expect("expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), - ExecutionConfig::for_test_transaction(), - ); - - // Assert - receipt.expect_commit_success(); -} From f6fb59af17ba5462ad04a1f88a58297b8d46b2df Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 16:56:43 -0500 Subject: [PATCH 07/12] Add subintent auth tests --- radix-engine-tests/tests/system/mod.rs | 1 + .../tests/system/subintent_auth.rs | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 radix-engine-tests/tests/system/subintent_auth.rs diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index caddfe4d30f..71e94e28cab 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -75,3 +75,4 @@ mod vault; mod vault_burn; mod vault_freeze; mod worktop; +mod subintent_auth; diff --git a/radix-engine-tests/tests/system/subintent_auth.rs b/radix-engine-tests/tests/system/subintent_auth.rs new file mode 100644 index 00000000000..84e60f76781 --- /dev/null +++ b/radix-engine-tests/tests/system/subintent_auth.rs @@ -0,0 +1,85 @@ +use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId, XRD}; +use radix_engine::errors::{RuntimeError, SystemModuleError}; +use radix_engine::transaction::ExecutionConfig; +use radix_engine_interface::macros::dec; +use radix_rust::btreeset; +use radix_transactions::builder::ManifestV2Builder; +use radix_transactions::model::{ManifestIntent, TestTransaction}; +use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; + +#[test] +fn should_not_be_able_to_use_root_auth_in_subintent() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + + // Act + let intents = vec![ + { + let manifest = ManifestV2Builder::new_v2() + .lock_standard_test_fee(account) + .yield_to_child(ManifestIntent(0), ()) + .build(); + + (manifest, ledger.next_transaction_nonce(), vec![1]) + }, + { + let manifest = ManifestV2Builder::new_v2() + .withdraw_from_account(account, XRD, dec!(10)) + .deposit_entire_worktop(account) + .build(); + + (manifest, ledger.next_transaction_nonce(), vec![]) + } + ]; + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_specific_failure(|e| matches!(e, RuntimeError::SystemModuleError(SystemModuleError::AuthError(..)))); +} + + +#[test] +fn should_be_able_to_use_separate_auth_in_subintent() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + + // Act + let intents = vec![ + { + let manifest = ManifestV2Builder::new_v2() + .lock_standard_test_fee(account) + .yield_to_child(ManifestIntent(0), ()) + .build(); + + (manifest, ledger.next_transaction_nonce(), vec![1]) + }, + { + let manifest = ManifestV2Builder::new_v2() + .withdraw_from_account(account, XRD, dec!(10)) + .deposit_entire_worktop(account) + .build(); + + (manifest, ledger.next_transaction_nonce(), vec![]) + } + ]; + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_commit_success(); +} From 4e34795c8cdb5b121c2ceb74a69c835680744368 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 17:15:12 -0500 Subject: [PATCH 08/12] Add subintent auth tests --- .../tests/blueprints/account_locker.rs | 5 +- .../tests/kernel/kernel_open_substate.rs | 3 +- .../kernel/transaction_multi_threaded.rs | 40 ++++--- .../tests/protocol/transaction.rs | 9 +- radix-engine-tests/tests/system/mod.rs | 2 +- .../tests/system/subintent_auth.rs | 49 ++++++--- .../tests/system/subintent_txn_shape.rs | 10 +- .../tests/system/transaction_limits.rs | 15 ++- .../transaction_processor/instructions.rs | 1 - .../src/model/v1/test_transaction.rs | 101 ++++++++++++------ .../src/ledger_simulator/ledger_simulator.rs | 48 ++++++--- 11 files changed, 188 insertions(+), 95 deletions(-) diff --git a/radix-engine-tests/tests/blueprints/account_locker.rs b/radix-engine-tests/tests/blueprints/account_locker.rs index 0bf43282371..e2069a89340 100644 --- a/radix-engine-tests/tests/blueprints/account_locker.rs +++ b/radix-engine-tests/tests/blueprints/account_locker.rs @@ -3059,9 +3059,10 @@ pub impl DefaultLedgerSimulator { }); let nonce = self.next_transaction_nonce(); - let test_transaction = TestTransaction::new_v1_from_nonce(manifest, nonce); + let test_transaction = + TestTransaction::new_v1_from_nonce(manifest, nonce, Default::default()); let prepared_transaction = test_transaction.prepare().unwrap(); - let executable = prepared_transaction.get_executable(Default::default()); + let executable = prepared_transaction.get_executable(); self.execute_transaction(executable, execution_config) } } diff --git a/radix-engine-tests/tests/kernel/kernel_open_substate.rs b/radix-engine-tests/tests/kernel/kernel_open_substate.rs index c76b3ff8b72..60c2fd7413a 100644 --- a/radix-engine-tests/tests/kernel/kernel_open_substate.rs +++ b/radix-engine-tests/tests/kernel/kernel_open_substate.rs @@ -34,10 +34,11 @@ pub fn test_open_substate_of_invisible_package_address() { let transaction = TestTransaction::new_v1_from_nonce( ManifestBuilder::new().lock_fee_from_faucet().build(), 1, + btreeset![], ) .prepare() .unwrap(); - let executable = transaction.get_executable(btreeset![]); + let executable = transaction.get_executable(); // Create database and bootstrap let mut database = InMemorySubstateDatabase::standard(); diff --git a/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs b/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs index 9b4eb48c1aa..add280a5adf 100644 --- a/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs +++ b/radix-engine-tests/tests/kernel/transaction_multi_threaded.rs @@ -44,12 +44,14 @@ mod multi_threaded_test { &mut substate_db, &vm_modules, &ExecutionConfig::for_test_transaction(), - TestTransaction::new_v1(manifest, hash(format!("Account creation: {i}"))) - .prepare() - .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key( - &public_key - )]), + TestTransaction::new_v1( + manifest, + hash(format!("Account creation: {i}")), + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + .prepare() + .unwrap() + .get_executable(), ) .expect_commit(true) .new_component_addresses()[0]; @@ -71,10 +73,14 @@ mod multi_threaded_test { &mut substate_db, &vm_modules, &ExecutionConfig::for_test_transaction(), - TestTransaction::new_v1(manifest.clone(), hash(format!("Fill account: {}", nonce))) - .prepare() - .expect("Expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + TestTransaction::new_v1( + manifest.clone(), + hash(format!("Fill account: {}", nonce)), + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + .prepare() + .expect("Expected transaction to be preparable") + .get_executable(), ) .expect_commit(true); } @@ -96,12 +102,14 @@ mod multi_threaded_test { &substate_db, &vm_modules, &ExecutionConfig::for_test_transaction(), - TestTransaction::new_v1(manifest.clone(), hash(format!("Transfer"))) - .prepare() - .expect("Expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key( - &public_key, - )]), + TestTransaction::new_v1( + manifest.clone(), + hash(format!("Transfer")), + btreeset![NonFungibleGlobalId::from_public_key(&public_key,)], + ) + .prepare() + .expect("Expected transaction to be preparable") + .get_executable(), ); receipt.expect_commit_success(); println!("receipt = {:?}", receipt); diff --git a/radix-engine-tests/tests/protocol/transaction.rs b/radix-engine-tests/tests/protocol/transaction.rs index 27875fcf2e7..2dfa2c17d8c 100644 --- a/radix-engine-tests/tests/protocol/transaction.rs +++ b/radix-engine-tests/tests/protocol/transaction.rs @@ -19,14 +19,19 @@ fn bottlenose_protocol_should_not_support_v2_transactions() { let manifest = ManifestV2Builder::new_v2() .lock_standard_test_fee(account) .build(); - (manifest, ledger.next_transaction_nonce(), vec![]) + ( + manifest, + ledger.next_transaction_nonce(), + vec![], + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) }) .collect(); let receipt = ledger.execute_transaction( TestTransaction::new_v2_from_nonce(intents) .prepare() .expect("expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(), ExecutionConfig::for_test_transaction(), ); diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index 71e94e28cab..f2708791b5c 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -51,6 +51,7 @@ mod royalty_auth; mod royalty_edge_cases; mod schema_sanity_check; +mod subintent_auth; mod subintent_txn_shape; mod system; mod system_access_rule; @@ -75,4 +76,3 @@ mod vault; mod vault_burn; mod vault_freeze; mod worktop; -mod subintent_auth; diff --git a/radix-engine-tests/tests/system/subintent_auth.rs b/radix-engine-tests/tests/system/subintent_auth.rs index 84e60f76781..6d061133b00 100644 --- a/radix-engine-tests/tests/system/subintent_auth.rs +++ b/radix-engine-tests/tests/system/subintent_auth.rs @@ -21,7 +21,12 @@ fn should_not_be_able_to_use_root_auth_in_subintent() { .yield_to_child(ManifestIntent(0), ()) .build(); - (manifest, ledger.next_transaction_nonce(), vec![1]) + ( + manifest, + ledger.next_transaction_nonce(), + vec![1], + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) }, { let manifest = ManifestV2Builder::new_v2() @@ -29,28 +34,38 @@ fn should_not_be_able_to_use_root_auth_in_subintent() { .deposit_entire_worktop(account) .build(); - (manifest, ledger.next_transaction_nonce(), vec![]) - } + ( + manifest, + ledger.next_transaction_nonce(), + vec![], + btreeset!(), + ) + }, ]; let receipt = ledger.execute_transaction( TestTransaction::new_v2_from_nonce(intents) .prepare() .expect("expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(), ExecutionConfig::for_test_transaction(), ); // Assert - receipt.expect_specific_failure(|e| matches!(e, RuntimeError::SystemModuleError(SystemModuleError::AuthError(..)))); + receipt.expect_specific_failure(|e| { + matches!( + e, + RuntimeError::SystemModuleError(SystemModuleError::AuthError(..)) + ) + }); } - #[test] fn should_be_able_to_use_separate_auth_in_subintent() { // Arrange let mut ledger = LedgerSimulatorBuilder::new().build(); let (public_key, _, account) = ledger.new_allocated_account(); + let (public_key2, _, account2) = ledger.new_allocated_account(); // Act let intents = vec![ @@ -60,23 +75,33 @@ fn should_be_able_to_use_separate_auth_in_subintent() { .yield_to_child(ManifestIntent(0), ()) .build(); - (manifest, ledger.next_transaction_nonce(), vec![1]) + ( + manifest, + ledger.next_transaction_nonce(), + vec![1], + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) }, { let manifest = ManifestV2Builder::new_v2() - .withdraw_from_account(account, XRD, dec!(10)) - .deposit_entire_worktop(account) + .withdraw_from_account(account2, XRD, dec!(10)) + .deposit_entire_worktop(account2) .build(); - (manifest, ledger.next_transaction_nonce(), vec![]) - } + ( + manifest, + ledger.next_transaction_nonce(), + vec![], + btreeset![NonFungibleGlobalId::from_public_key(&public_key2)], + ) + }, ]; let receipt = ledger.execute_transaction( TestTransaction::new_v2_from_nonce(intents) .prepare() .expect("expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(), ExecutionConfig::for_test_transaction(), ); diff --git a/radix-engine-tests/tests/system/subintent_txn_shape.rs b/radix-engine-tests/tests/system/subintent_txn_shape.rs index 29aa08121d3..5cce33e7217 100644 --- a/radix-engine-tests/tests/system/subintent_txn_shape.rs +++ b/radix-engine-tests/tests/system/subintent_txn_shape.rs @@ -39,14 +39,20 @@ fn test_subintent_txn_shape(children: Vec>) { let manifest = builder.build(); - intents.push((manifest, ledger.next_transaction_nonce(), intent_children)) + let signatures = btreeset![NonFungibleGlobalId::from_public_key(&public_key)]; + intents.push(( + manifest, + ledger.next_transaction_nonce(), + intent_children, + signatures, + )) } let receipt = ledger.execute_transaction( TestTransaction::new_v2_from_nonce(intents) .prepare() .expect("expected transaction to be preparable") - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + .get_executable(), ExecutionConfig::for_test_transaction(), ); diff --git a/radix-engine-tests/tests/system/transaction_limits.rs b/radix-engine-tests/tests/system/transaction_limits.rs index 057cf6267a0..8b1994a8f84 100644 --- a/radix-engine-tests/tests/system/transaction_limits.rs +++ b/radix-engine-tests/tests/system/transaction_limits.rs @@ -44,7 +44,7 @@ fn test_read_non_existent_entries_from_kv_store_exceeding_limit() { ) .build(); - let transactions = TestTransaction::new_v1_from_nonce(manifest, 10); + let transactions = TestTransaction::new_v1_from_nonce(manifest, 10, btreeset!()); let prepared = transactions.prepare().unwrap(); let execution_config = { @@ -61,8 +61,7 @@ fn test_read_non_existent_entries_from_kv_store_exceeding_limit() { execution_config }; - let receipt = - ledger.execute_transaction(prepared.get_executable(btreeset!()), execution_config); + let receipt = ledger.execute_transaction(prepared.get_executable(), execution_config); // Assert receipt.expect_specific_failure(|e| { @@ -111,7 +110,7 @@ fn test_write_entries_to_kv_store_exceeding_limit() { ) .build(); - let transactions = TestTransaction::new_v1_from_nonce(manifest, 10); + let transactions = TestTransaction::new_v1_from_nonce(manifest, 10, btreeset!()); let prepared = transactions.prepare().unwrap(); let execution_config = { let mut execution_config = ExecutionConfig::for_test_transaction(); @@ -128,8 +127,7 @@ fn test_write_entries_to_kv_store_exceeding_limit() { execution_config }; - let receipt = - ledger.execute_transaction(prepared.get_executable(btreeset!()), execution_config); + let receipt = ledger.execute_transaction(prepared.get_executable(), execution_config); // Assert receipt.expect_specific_failure(|e| { @@ -162,7 +160,7 @@ fn test_write_entries_to_heap_kv_store_exceeding_limit() { ) .build(); - let transactions = TestTransaction::new_v1_from_nonce(manifest, 10); + let transactions = TestTransaction::new_v1_from_nonce(manifest, 10, btreeset!()); let prepared = transactions.prepare().unwrap(); let execution_config = { let mut execution_config = ExecutionConfig::for_test_transaction(); @@ -178,8 +176,7 @@ fn test_write_entries_to_heap_kv_store_exceeding_limit() { execution_config }; - let receipt = - ledger.execute_transaction(prepared.get_executable(btreeset!()), execution_config); + let receipt = ledger.execute_transaction(prepared.get_executable(), execution_config); // Assert receipt.expect_specific_failure(|e| { diff --git a/radix-engine/src/blueprints/transaction_processor/instructions.rs b/radix-engine/src/blueprints/transaction_processor/instructions.rs index 6b46d6ef36c..f189962e9e3 100644 --- a/radix-engine/src/blueprints/transaction_processor/instructions.rs +++ b/radix-engine/src/blueprints/transaction_processor/instructions.rs @@ -15,7 +15,6 @@ use radix_rust::prelude::*; use radix_transactions::data::transform; use radix_transactions::manifest::*; use radix_transactions::model::{InstructionV1, InstructionV2}; -use radix_transactions::prelude::*; pub enum Yield { ToChild(usize), diff --git a/radix-transactions/src/model/v1/test_transaction.rs b/radix-transactions/src/model/v1/test_transaction.rs index 8ab1a24d18e..dc832c2cee1 100644 --- a/radix-transactions/src/model/v1/test_transaction.rs +++ b/radix-transactions/src/model/v1/test_transaction.rs @@ -17,6 +17,7 @@ pub struct TestIntentV1 { pub instructions: InstructionsV1, pub blobs: BlobsV1, pub hash: Hash, + pub initial_proofs: BTreeSet, } #[derive(ManifestSbor)] @@ -24,6 +25,7 @@ pub struct TestIntentV2 { pub instructions: InstructionsV2, pub blobs: BlobsV1, pub hash: Hash, + pub initial_proofs: BTreeSet, pub children_intent_indices: Vec, } @@ -40,49 +42,83 @@ pub struct PreparedTestIntent { pub blobs: Rc>>, pub hash: Hash, pub children_intent_indices: Vec, + pub initial_proofs: BTreeSet, } impl TestTransaction { /// The nonce needs to be globally unique amongst test transactions on your ledger - pub fn new_v1_from_nonce(manifest: TransactionManifestV1, nonce: u32) -> Self { - Self::new_v1(manifest, hash(format!("Test transaction: {}", nonce))) + pub fn new_v1_from_nonce( + manifest: TransactionManifestV1, + nonce: u32, + initial_proofs: BTreeSet, + ) -> Self { + Self::new_v1( + manifest, + hash(format!("Test transaction: {}", nonce)), + initial_proofs, + ) } - pub fn new_v1(manifest: TransactionManifestV1, hash: Hash) -> Self { + pub fn new_v1( + manifest: TransactionManifestV1, + hash: Hash, + initial_proofs: BTreeSet, + ) -> Self { let (instructions, blobs) = manifest.for_intent(); Self::V1(TestIntentV1 { instructions, blobs, hash, + initial_proofs, }) } - pub fn new_v2_from_nonce(intents: Vec<(TransactionManifestV2, u32, Vec)>) -> Self { + pub fn new_v2_from_nonce( + intents: Vec<( + TransactionManifestV2, + u32, + Vec, + BTreeSet, + )>, + ) -> Self { let intents = intents .into_iter() - .map(|(manifest, nonce, children_intent_indices)| { - ( - manifest, - hash(format!("Test transaction: {}", nonce)), - children_intent_indices, - ) - }) + .map( + |(manifest, nonce, children_intent_indices, initial_proofs)| { + ( + manifest, + hash(format!("Test transaction: {}", nonce)), + children_intent_indices, + initial_proofs, + ) + }, + ) .collect(); Self::new_v2(intents) } - pub fn new_v2(intents: Vec<(TransactionManifestV2, Hash, Vec)>) -> Self { + pub fn new_v2( + intents: Vec<( + TransactionManifestV2, + Hash, + Vec, + BTreeSet, + )>, + ) -> Self { let intents = intents .into_iter() - .map(|(manifest, hash, children_intent_indices)| { - let (instructions, blobs, ..) = manifest.for_intent(); - TestIntentV2 { - instructions, - blobs, - hash, - children_intent_indices, - } - }) + .map( + |(manifest, hash, children_intent_indices, initial_proofs)| { + let (instructions, blobs, ..) = manifest.for_intent(); + TestIntentV2 { + instructions, + blobs, + hash, + children_intent_indices, + initial_proofs, + } + }, + ) .collect(); Self::V2(intents) @@ -99,6 +135,7 @@ impl TestTransaction { blobs: intent.blobs.prepare_partial()?.blobs_by_hash, hash: intent.hash, children_intent_indices: vec![], + initial_proofs: intent.initial_proofs, })) } Self::V2(intents) => { @@ -113,6 +150,7 @@ impl TestTransaction { blobs: intent.blobs.prepare_partial()?.blobs_by_hash, hash: intent.hash, children_intent_indices: intent.children_intent_indices, + initial_proofs: intent.initial_proofs, }); } @@ -123,16 +161,13 @@ impl TestTransaction { } impl PreparedTestTransaction { - pub fn get_executable( - &self, - initial_proofs: BTreeSet, - ) -> ExecutableTransaction { + pub fn get_executable(&self) -> ExecutableTransaction { match self { PreparedTestTransaction::V1(intent) => { - let num_of_signature_validations = initial_proofs.len() + 1; + let num_of_signature_validations = intent.initial_proofs.len() + 1; ExecutableTransaction::new_v1( intent.encoded_instructions.clone(), - AuthZoneInit::proofs(initial_proofs), + AuthZoneInit::proofs(intent.initial_proofs.clone()), intent.references.clone(), intent.blobs.clone(), ExecutionContext { @@ -163,7 +198,11 @@ impl PreparedTestTransaction { + intent.blobs.values().map(|x| x.len()).sum::() }) .sum(); - let num_of_signature_validations = initial_proofs.len() + 1; + let num_of_signature_validations = intents + .iter() + .map(|intent| intent.initial_proofs.len()) + .sum(); + let context = ExecutionContext { unique_hash: intents.get(0).unwrap().hash, intent_hash_nullifications: vec![], @@ -181,14 +220,10 @@ impl PreparedTestTransaction { start_timestamp_inclusive: None, end_timestamp_exclusive: None, }; - let mut root_auth_zone = Some(AuthZoneInit::proofs(initial_proofs)); let intents = intents .iter() .map(|intent| { - // TODO - let auth_zone_init = root_auth_zone - .take() - .unwrap_or(AuthZoneInit::proofs(Default::default())); + let auth_zone_init = AuthZoneInit::proofs(intent.initial_proofs.clone()); ExecutableIntent { encoded_instructions: intent.encoded_instructions.clone(), diff --git a/scrypto-test/src/ledger_simulator/ledger_simulator.rs b/scrypto-test/src/ledger_simulator/ledger_simulator.rs index 08f80ceefcc..a62a1051b39 100644 --- a/scrypto-test/src/ledger_simulator/ledger_simulator.rs +++ b/scrypto-test/src/ledger_simulator/ledger_simulator.rs @@ -1136,10 +1136,14 @@ impl LedgerSimulator { { let nonce = self.next_transaction_nonce(); self.execute_transaction( - TestTransaction::new_v1_from_nonce(manifest, nonce) - .prepare() - .expect("expected transaction to be preparable") - .get_executable(initial_proofs.into_iter().collect()), + TestTransaction::new_v1_from_nonce( + manifest, + nonce, + initial_proofs.into_iter().collect(), + ) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(), ExecutionConfig::for_test_transaction(), ) } @@ -1155,10 +1159,14 @@ impl LedgerSimulator { { let nonce = self.next_transaction_nonce(); self.execute_transaction( - TestTransaction::new_v1_from_nonce(manifest, nonce) - .prepare() - .expect("expected transaction to be preparable") - .get_executable(initial_proofs.into_iter().collect()), + TestTransaction::new_v1_from_nonce( + manifest, + nonce, + initial_proofs.into_iter().collect(), + ) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(), execution_config, ) } @@ -1179,10 +1187,14 @@ impl LedgerSimulator { ..Default::default() }); self.execute_transaction( - TestTransaction::new_v1_from_nonce(manifest, nonce) - .prepare() - .expect("expected transaction to be preparable") - .get_executable(initial_proofs.into_iter().collect()), + TestTransaction::new_v1_from_nonce( + manifest, + nonce, + initial_proofs.into_iter().collect(), + ) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(), config, ) } @@ -1197,10 +1209,14 @@ impl LedgerSimulator { T: IntoIterator, { let nonce = self.next_transaction_nonce(); - let txn = TestTransaction::new_v1_from_nonce(manifest, nonce) - .prepare() - .expect("expected transaction to be preparable"); - let executable = txn.get_executable(initial_proofs.into_iter().collect()); + let txn = TestTransaction::new_v1_from_nonce( + manifest, + nonce, + initial_proofs.into_iter().collect(), + ) + .prepare() + .expect("expected transaction to be preparable"); + let executable = txn.get_executable(); let vm_init = self.vm_modules.create_vm_init(); From 03a0aac5893a4c4bbed82c64670f59a41e64ec79 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 18:20:03 -0500 Subject: [PATCH 09/12] Some renames --- radix-clis/src/resim/mod.rs | 4 +-- radix-engine-tests/benches/transfer.rs | 36 ++++++++++++------- radix-engine-tests/tests/kernel/panics.rs | 4 +-- radix-engine-tests/tests/system/mod.rs | 1 - .../transaction_processor/blueprint.rs | 12 +++---- .../blueprints/transaction_processor/mod.rs | 3 +- radix-engine/src/kernel/kernel.rs | 4 +-- radix-engine/src/kernel/kernel_api.rs | 12 +++---- radix-engine/src/system/mod.rs | 2 +- radix-engine/src/system/system_callback.rs | 4 +-- radix-engine/src/system/transaction/mod.rs | 2 ++ .../multithread_txn_processor.rs} | 21 ++++++----- .../transaction/txn_processor.rs} | 18 +++++++--- .../ledger_simulator/inject_costing_err.rs | 6 ++-- 14 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 radix-engine/src/system/transaction/mod.rs rename radix-engine/src/system/{txn_threads.rs => transaction/multithread_txn_processor.rs} (89%) rename radix-engine/src/{blueprints/transaction_processor/tx_processor.rs => system/transaction/txn_processor.rs} (97%) diff --git a/radix-clis/src/resim/mod.rs b/radix-clis/src/resim/mod.rs index b62d3107862..bd716791fd9 100644 --- a/radix-clis/src/resim/mod.rs +++ b/radix-clis/src/resim/mod.rs @@ -222,7 +222,7 @@ pub fn handle_manifest( .map(|e| NonFungibleGlobalId::from_public_key(&e.public_key())) .collect::>(); let nonce = get_nonce()?; - let transaction = TestTransaction::new_v1_from_nonce(manifest, nonce); + let transaction = TestTransaction::new_v1_from_nonce(manifest, nonce, initial_proofs); let receipt = execute_and_commit_transaction( &mut db, @@ -231,7 +231,7 @@ pub fn handle_manifest( transaction .prepare() .map_err(Error::TransactionPrepareError)? - .get_executable(initial_proofs), + .get_executable(), ); if print_receipt { diff --git a/radix-engine-tests/benches/transfer.rs b/radix-engine-tests/benches/transfer.rs index f6c4de2b07a..bf60a7db288 100644 --- a/radix-engine-tests/benches/transfer.rs +++ b/radix-engine-tests/benches/transfer.rs @@ -36,10 +36,14 @@ fn bench_transfer(c: &mut Criterion) { &mut substate_db, &vm_modules, &ExecutionConfig::for_notarized_transaction(NetworkDefinition::simulator()), - TestTransaction::new_v1_from_nonce(manifest, 1) - .prepare() - .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + TestTransaction::new_v1_from_nonce( + manifest, + 1, + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + .prepare() + .unwrap() + .get_executable(), ) .expect_commit(true) .new_component_addresses()[0]; @@ -62,10 +66,14 @@ fn bench_transfer(c: &mut Criterion) { &mut substate_db, &vm_modules, &ExecutionConfig::for_notarized_transaction(NetworkDefinition::simulator()), - TestTransaction::new_v1_from_nonce(manifest.clone(), nonce) - .prepare() - .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + TestTransaction::new_v1_from_nonce( + manifest.clone(), + nonce, + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + .prepare() + .unwrap() + .get_executable(), ) .expect_commit(true); } @@ -85,10 +93,14 @@ fn bench_transfer(c: &mut Criterion) { &mut substate_db, &vm_modules, &ExecutionConfig::for_notarized_transaction(NetworkDefinition::simulator()), - TestTransaction::new_v1_from_nonce(manifest.clone(), nonce) - .prepare() - .unwrap() - .get_executable(btreeset![NonFungibleGlobalId::from_public_key(&public_key)]), + TestTransaction::new_v1_from_nonce( + manifest.clone(), + nonce, + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + .prepare() + .unwrap() + .get_executable(), ); receipt.expect_commit_success(); nonce += 1; diff --git a/radix-engine-tests/tests/kernel/panics.rs b/radix-engine-tests/tests/kernel/panics.rs index b7cf5c52f32..11248e10a25 100644 --- a/radix-engine-tests/tests/kernel/panics.rs +++ b/radix-engine-tests/tests/kernel/panics.rs @@ -42,10 +42,10 @@ impl KernelApi for MockKernel { type CallbackObject = System; } -impl KernelThreadApi for MockKernel { +impl KernelStackApi for MockKernel { type CallFrameData = Actor; - fn kernel_switch_thread(&mut self, _id: usize) -> Result<(), RuntimeError> { + fn kernel_switch_stack(&mut self, _id: usize) -> Result<(), RuntimeError> { panic1!() } diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index f2708791b5c..698fca8fd30 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -50,7 +50,6 @@ mod royalty; mod royalty_auth; mod royalty_edge_cases; mod schema_sanity_check; - mod subintent_auth; mod subintent_txn_shape; mod system; diff --git a/radix-engine/src/blueprints/transaction_processor/blueprint.rs b/radix-engine/src/blueprints/transaction_processor/blueprint.rs index 2345d2dcc8a..8f9d2928f1a 100644 --- a/radix-engine/src/blueprints/transaction_processor/blueprint.rs +++ b/radix-engine/src/blueprints/transaction_processor/blueprint.rs @@ -1,4 +1,4 @@ -use crate::blueprints::transaction_processor::TxnProcessorThread; +use crate::blueprints::transaction_processor::{ResumeResult, TxnProcessorThread}; use crate::errors::RuntimeError; use crate::internal_prelude::{Sbor, ScryptoEncode, ScryptoSbor}; use crate::kernel::kernel_api::{KernelNodeApi, KernelSubstateApi}; @@ -57,17 +57,17 @@ impl TransactionProcessorBlueprint { TransactionProcessorV1MinorVersion::Zero => usize::MAX, TransactionProcessorV1MinorVersion::One => MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, }; - let mut txn_processor = TxnProcessorThread::::init( + let mut txn_processor_single_thread = TxnProcessorThread::::init( Rc::new(manifest_encoded_instructions), global_address_reservations, Rc::new(blobs), max_total_size_of_blobs, api, )?; - let yield_inst = txn_processor.resume(api)?; - if yield_inst.is_some() { - panic!("This should never yield"); + let resume_result = txn_processor_single_thread.resume(api)?; + if !matches!(resume_result, ResumeResult::Done) { + panic!("Unexpected yield occurred in v1 transaction processing"); } - Ok(txn_processor.outputs) + Ok(txn_processor_single_thread.outputs) } } diff --git a/radix-engine/src/blueprints/transaction_processor/mod.rs b/radix-engine/src/blueprints/transaction_processor/mod.rs index c07d0c93e42..f293a45ceb3 100644 --- a/radix-engine/src/blueprints/transaction_processor/mod.rs +++ b/radix-engine/src/blueprints/transaction_processor/mod.rs @@ -1,9 +1,8 @@ mod blueprint; mod instructions; mod package; -mod tx_processor; +pub use crate::system::transaction::txn_processor::*; pub use blueprint::*; pub use instructions::*; pub use package::*; -pub use tx_processor::*; diff --git a/radix-engine/src/kernel/kernel.rs b/radix-engine/src/kernel/kernel.rs index d7fa8262446..7fba828e239 100644 --- a/radix-engine/src/kernel/kernel.rs +++ b/radix-engine/src/kernel/kernel.rs @@ -1184,10 +1184,10 @@ where } } -impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelThreadApi for Kernel<'g, M, S> { +impl<'g, M: KernelCallbackObject, S: CommitableSubstateStore> KernelStackApi for Kernel<'g, M, S> { type CallFrameData = M::CallFrameData; - fn kernel_switch_thread(&mut self, id: usize) -> Result<(), RuntimeError> { + fn kernel_switch_stack(&mut self, id: usize) -> Result<(), RuntimeError> { self.stacks.switch(id); Ok(()) } diff --git a/radix-engine/src/kernel/kernel_api.rs b/radix-engine/src/kernel/kernel_api.rs index 9ac208cb891..19128e5a82d 100644 --- a/radix-engine/src/kernel/kernel_api.rs +++ b/radix-engine/src/kernel/kernel_api.rs @@ -171,14 +171,14 @@ pub trait KernelInvokeApi { ) -> Result; } -/// API for managing threads and their associated call frame stack -pub trait KernelThreadApi { +/// API for managing multiple call frame stacks +pub trait KernelStackApi { type CallFrameData; - /// Context switches to a different thread - fn kernel_switch_thread(&mut self, id: usize) -> Result<(), RuntimeError>; + /// Achieves a context switch by switching the underlying callframe/stack + fn kernel_switch_stack(&mut self, id: usize) -> Result<(), RuntimeError>; - /// Sets the call frame data for the current thread's call frame + /// Sets the call frame data for the current call frame fn kernel_set_call_frame_data(&mut self, data: Self::CallFrameData) -> Result<(), RuntimeError>; @@ -227,7 +227,7 @@ pub trait KernelApi: KernelNodeApi + KernelSubstateApi<::LockData> + KernelInvokeApi<::CallFrameData> - + KernelThreadApi::CallFrameData> + + KernelStackApi::CallFrameData> + KernelInternalApi { type CallbackObject: KernelCallbackObject; diff --git a/radix-engine/src/system/mod.rs b/radix-engine/src/system/mod.rs index 53321422697..967dfef667b 100644 --- a/radix-engine/src/system/mod.rs +++ b/radix-engine/src/system/mod.rs @@ -14,5 +14,5 @@ pub mod system_modules; pub mod system_substate_schemas; pub mod system_substates; pub mod system_type_checker; -pub mod txn_threads; +pub mod transaction; pub mod type_info; diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index 55ca67e6636..5cdb80f4096 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -32,7 +32,7 @@ use crate::system::system_modules::transaction_runtime::TransactionRuntimeModule use crate::system::system_modules::{EnabledModules, SystemModuleMixer}; use crate::system::system_substates::KeyValueEntrySubstate; use crate::system::system_type_checker::{BlueprintTypeTarget, KVStoreTypeTarget}; -use crate::system::txn_threads::TxnThreads; +use crate::system::transaction::multithread_txn_processor::MultiThreadedTxnProcessor; use crate::track::*; use crate::transaction::*; use radix_blueprint_schema_init::RefTypes; @@ -133,7 +133,7 @@ impl VersionedSystemLogic { } VersionedSystemLogic::V2 => { let mut txn_threads = - TxnThreads::init(executable, global_address_reservations, api)?; + MultiThreadedTxnProcessor::init(executable, global_address_reservations, api)?; txn_threads.execute(api)?; let output = txn_threads .threads diff --git a/radix-engine/src/system/transaction/mod.rs b/radix-engine/src/system/transaction/mod.rs new file mode 100644 index 00000000000..6ee6f696b82 --- /dev/null +++ b/radix-engine/src/system/transaction/mod.rs @@ -0,0 +1,2 @@ +pub mod multithread_txn_processor; +pub mod txn_processor; diff --git a/radix-engine/src/system/txn_threads.rs b/radix-engine/src/system/transaction/multithread_txn_processor.rs similarity index 89% rename from radix-engine/src/system/txn_threads.rs rename to radix-engine/src/system/transaction/multithread_txn_processor.rs index 9881a37a56b..716a7ae1cda 100644 --- a/radix-engine/src/system/txn_threads.rs +++ b/radix-engine/src/system/transaction/multithread_txn_processor.rs @@ -1,5 +1,5 @@ use crate::blueprints::transaction_processor::{ - TxnProcessorThread, Yield, MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, + ResumeResult, TxnProcessorThread, MAX_TOTAL_BLOB_SIZE_PER_INVOCATION, }; use crate::errors::{KernelError, RuntimeError}; use crate::kernel::kernel_callback_api::KernelCallbackObject; @@ -12,13 +12,16 @@ use radix_common::prelude::{BlueprintId, GlobalAddressReservation}; use radix_engine_interface::blueprints::transaction_processor::{ TRANSACTION_PROCESSOR_BLUEPRINT, TRANSACTION_PROCESSOR_RUN_IDENT, }; +use radix_rust::prelude::*; use radix_transactions::model::{ExecutableTransaction, InstructionV2}; +use sbor::prelude::ToString; -pub struct TxnThreads { +/// Multi-thread transaction processor for executing multiple subintents +pub struct MultiThreadedTxnProcessor { pub threads: Vec<(TxnProcessorThread, Vec)>, } -impl TxnThreads { +impl MultiThreadedTxnProcessor { pub fn init( executable: ExecutableTransaction, global_address_reservations: Vec, @@ -29,7 +32,7 @@ impl TxnThreads { // Setup let intents = executable.intents(); for (thread_id, intent) in intents.iter().enumerate() { - api.kernel_switch_thread(thread_id)?; + api.kernel_switch_stack(thread_id)?; let mut system_service = SystemService::new(api); let virtual_resources = intent @@ -75,20 +78,20 @@ impl TxnThreads { let mut parent_stack = vec![]; loop { - api.kernel_switch_thread(cur_thread)?; + api.kernel_switch_stack(cur_thread)?; let (txn_thread, children_mapping) = self.threads.get_mut(cur_thread).unwrap(); let mut system_service = SystemService::new(api); match txn_thread.resume(&mut system_service)? { - Some(Yield::ToChild(child)) => { + ResumeResult::YieldToChild(child) => { let child = *children_mapping.get(child).unwrap(); parent_stack.push(cur_thread); cur_thread = child; } - Some(Yield::ToParent) => { + ResumeResult::YieldToParent => { cur_thread = parent_stack.pop().unwrap(); } - None => { + ResumeResult::Done => { if let Some(parent) = parent_stack.pop() { cur_thread = parent; } else { @@ -105,7 +108,7 @@ impl TxnThreads { pub fn cleanup(self, api: &mut Y) -> Result<(), RuntimeError> { for (thread_id, _intent) in self.threads.iter().enumerate() { - api.kernel_switch_thread(thread_id)?; + api.kernel_switch_stack(thread_id)?; let owned_nodes = api.kernel_get_owned_nodes()?; System::auto_drop(owned_nodes, api)?; diff --git a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs b/radix-engine/src/system/transaction/txn_processor.rs similarity index 97% rename from radix-engine/src/blueprints/transaction_processor/tx_processor.rs rename to radix-engine/src/system/transaction/txn_processor.rs index 8ac0df5df9c..0ff953f527b 100644 --- a/radix-engine/src/blueprints/transaction_processor/tx_processor.rs +++ b/radix-engine/src/system/transaction/txn_processor.rs @@ -42,6 +42,12 @@ impl From for RuntimeError { } } +pub enum ResumeResult { + YieldToChild(usize), + YieldToParent, + Done, +} + pub struct TxnProcessorThread { instructions: VecDeque, worktop: Worktop, @@ -106,21 +112,25 @@ impl TxnProcessorThread pub fn resume + KernelNodeApi + KernelSubstateApi, L: Default>( &mut self, api: &mut Y, - ) -> Result, RuntimeError> { + ) -> Result { while let Some(instruction) = self.instructions.pop_front() { api.update_instruction_index(self.instruction_index)?; let (output, yield_instruction) = instruction.execute(&mut self.worktop, &mut self.objects, api)?; self.outputs.push(output); self.instruction_index += 1; - if yield_instruction.is_some() { - return Ok(yield_instruction); + if let Some(yield_instruction) = yield_instruction { + let result = match yield_instruction { + Yield::ToChild(child) => ResumeResult::YieldToChild(child), + Yield::ToParent => ResumeResult::YieldToParent, + }; + return Ok(result); } } self.worktop.drop(api)?; - Ok(None) + Ok(ResumeResult::Done) } } diff --git a/scrypto-test/src/ledger_simulator/inject_costing_err.rs b/scrypto-test/src/ledger_simulator/inject_costing_err.rs index 032c92bc642..739bf211153 100644 --- a/scrypto-test/src/ledger_simulator/inject_costing_err.rs +++ b/scrypto-test/src/ledger_simulator/inject_costing_err.rs @@ -493,12 +493,12 @@ impl<'a, M: SystemCallbackObject + 'a, K: KernelApi>> - KernelThreadApi for WrappedKernelApi<'a, M, K> + KernelStackApi for WrappedKernelApi<'a, M, K> { type CallFrameData = Actor; - fn kernel_switch_thread(&mut self, id: usize) -> Result<(), RuntimeError> { - self.api.kernel_switch_thread(id) + fn kernel_switch_stack(&mut self, id: usize) -> Result<(), RuntimeError> { + self.api.kernel_switch_stack(id) } fn kernel_set_call_frame_data(&mut self, data: Actor) -> Result<(), RuntimeError> { From 592369e36d03f3b22c80b4438ed5455fd4a76bf9 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 18:35:55 -0500 Subject: [PATCH 10/12] Add subintent leak tests --- radix-engine-tests/tests/system/mod.rs | 1 + .../tests/system/subintent_leaks.rs | 113 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 radix-engine-tests/tests/system/subintent_leaks.rs diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index 698fca8fd30..4723a4041c0 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -51,6 +51,7 @@ mod royalty_auth; mod royalty_edge_cases; mod schema_sanity_check; mod subintent_auth; +mod subintent_leaks; mod subintent_txn_shape; mod system; mod system_access_rule; diff --git a/radix-engine-tests/tests/system/subintent_leaks.rs b/radix-engine-tests/tests/system/subintent_leaks.rs new file mode 100644 index 00000000000..06ef943fe55 --- /dev/null +++ b/radix-engine-tests/tests/system/subintent_leaks.rs @@ -0,0 +1,113 @@ +use radix_common::constants::XRD; +use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId}; +use radix_engine::blueprints::resource::FungibleResourceManagerError; +use radix_engine::errors::{ApplicationError, RuntimeError}; +use radix_engine::transaction::ExecutionConfig; +use radix_engine_interface::macros::dec; +use radix_rust::btreeset; +use radix_transactions::builder::ManifestV2Builder; +use radix_transactions::model::{ManifestIntent, TestTransaction}; +use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; + +#[test] +fn bucket_leak_in_subintent_should_fail() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + let (public_key2, _, account2) = ledger.new_allocated_account(); + + // Act + let intents = vec![ + { + let manifest = ManifestV2Builder::new_v2() + .lock_standard_test_fee(account) + .yield_to_child(ManifestIntent(0), ()) + .build(); + + ( + manifest, + ledger.next_transaction_nonce(), + vec![1], + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + }, + { + let manifest = ManifestV2Builder::new_v2() + .withdraw_from_account(account2, XRD, dec!(10)) + .build(); + + ( + manifest, + ledger.next_transaction_nonce(), + vec![], + btreeset![NonFungibleGlobalId::from_public_key(&public_key2)], + ) + }, + ]; + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_specific_failure(|e| { + matches!( + e, + RuntimeError::ApplicationError(ApplicationError::FungibleResourceManagerError( + FungibleResourceManagerError::DropNonEmptyBucket + )) + ) + }); +} + +#[test] +fn proofs_in_subintent_should_autodrop() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + let (public_key2, _, account2) = ledger.new_allocated_account(); + + // Act + let intents = vec![ + { + let manifest = ManifestV2Builder::new_v2() + .lock_standard_test_fee(account) + .yield_to_child(ManifestIntent(0), ()) + .build(); + + ( + manifest, + ledger.next_transaction_nonce(), + vec![1], + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + }, + { + let manifest = ManifestV2Builder::new_v2() + .create_proof_from_account_of_amount(account2, XRD, dec!(10)) + .build(); + + ( + manifest, + ledger.next_transaction_nonce(), + vec![], + btreeset![NonFungibleGlobalId::from_public_key(&public_key2)], + ) + }, + ]; + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_commit_success(); +} From 3d4d0a94a5bed195588de3a7d560982e2cfdc789 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 10 Sep 2024 20:58:25 -0500 Subject: [PATCH 11/12] Add more subintent tests --- radix-engine-tests/tests/blueprints/mod.rs | 1 - radix-engine-tests/tests/system/mod.rs | 1 + .../tests/system/subintent_auth.rs | 88 ++++++++++++++++++- .../transaction_tracker.rs | 0 4 files changed, 86 insertions(+), 4 deletions(-) rename radix-engine-tests/tests/{blueprints => system}/transaction_tracker.rs (100%) diff --git a/radix-engine-tests/tests/blueprints/mod.rs b/radix-engine-tests/tests/blueprints/mod.rs index fb3682918e0..888ecc05384 100644 --- a/radix-engine-tests/tests/blueprints/mod.rs +++ b/radix-engine-tests/tests/blueprints/mod.rs @@ -24,7 +24,6 @@ mod reentrancy; mod resource; mod static_dependencies; mod time; -mod transaction_tracker; mod tx_processor; mod tx_processor_access; mod validator; diff --git a/radix-engine-tests/tests/system/mod.rs b/radix-engine-tests/tests/system/mod.rs index 4723a4041c0..d9519eb8efa 100644 --- a/radix-engine-tests/tests/system/mod.rs +++ b/radix-engine-tests/tests/system/mod.rs @@ -71,6 +71,7 @@ mod toolkit_receipt; mod track; mod transaction_limits; mod transaction_runtime; +mod transaction_tracker; mod typed_substate_layout; mod vault; mod vault_burn; diff --git a/radix-engine-tests/tests/system/subintent_auth.rs b/radix-engine-tests/tests/system/subintent_auth.rs index 6d061133b00..0932787ed33 100644 --- a/radix-engine-tests/tests/system/subintent_auth.rs +++ b/radix-engine-tests/tests/system/subintent_auth.rs @@ -1,10 +1,18 @@ -use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId, XRD}; +use radix_common::constants::TRANSACTION_PROCESSOR_PACKAGE; +use radix_common::crypto::Hash; +use radix_common::prelude::{manifest_encode, FromPublicKey, NonFungibleGlobalId, XRD}; +use radix_common::ManifestSbor; use radix_engine::errors::{RuntimeError, SystemModuleError}; +use radix_engine::system::system_modules::auth::AuthError; use radix_engine::transaction::ExecutionConfig; +use radix_engine_interface::blueprints::transaction_processor::{ + TRANSACTION_PROCESSOR_BLUEPRINT, TRANSACTION_PROCESSOR_RUN_IDENT, +}; use radix_engine_interface::macros::dec; use radix_rust::btreeset; -use radix_transactions::builder::ManifestV2Builder; -use radix_transactions::model::{ManifestIntent, TestTransaction}; +use radix_rust::prelude::{index_map_new, IndexMap}; +use radix_transactions::builder::{ManifestBuilder, ManifestV2Builder}; +use radix_transactions::model::{InstructionV1, ManifestIntent, TestTransaction}; use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; #[test] @@ -108,3 +116,77 @@ fn should_be_able_to_use_separate_auth_in_subintent() { // Assert receipt.expect_commit_success(); } + +#[derive(Debug, Eq, PartialEq, ManifestSbor)] +pub struct ManifestTransactionProcessorRunInput { + pub manifest_encoded_instructions: Vec, + pub global_address_reservations: Vec<()>, + pub references: Vec<()>, + pub blobs: IndexMap>, +} + +#[test] +fn should_not_be_able_to_call_tx_processor_in_subintent() { + // Arrange + let mut ledger = LedgerSimulatorBuilder::new().build(); + let (public_key, _, account) = ledger.new_allocated_account(); + + // Act + let intents = vec![ + { + let manifest = ManifestV2Builder::new_v2() + .lock_standard_test_fee(account) + .yield_to_child(ManifestIntent(0), ()) + .build(); + + ( + manifest, + ledger.next_transaction_nonce(), + vec![1], + btreeset![NonFungibleGlobalId::from_public_key(&public_key)], + ) + }, + { + let instructions: Vec = Vec::new(); + let manifest_encoded_instructions = manifest_encode(&instructions).unwrap(); + let manifest = ManifestV2Builder::new_v2() + .call_function( + TRANSACTION_PROCESSOR_PACKAGE, + TRANSACTION_PROCESSOR_BLUEPRINT, + TRANSACTION_PROCESSOR_RUN_IDENT, + ManifestTransactionProcessorRunInput { + manifest_encoded_instructions, + global_address_reservations: vec![], + references: vec![], + blobs: index_map_new(), + }, + ) + .build(); + + ( + manifest, + ledger.next_transaction_nonce(), + vec![], + btreeset![], + ) + }, + ]; + + let receipt = ledger.execute_transaction( + TestTransaction::new_v2_from_nonce(intents) + .prepare() + .expect("expected transaction to be preparable") + .get_executable(), + ExecutionConfig::for_test_transaction(), + ); + + // Assert + receipt.expect_specific_failure(|e| { + matches!( + e, + RuntimeError::SystemModuleError(SystemModuleError::AuthError(AuthError::Unauthorized( + .. + ))) + ) + }); +} diff --git a/radix-engine-tests/tests/blueprints/transaction_tracker.rs b/radix-engine-tests/tests/system/transaction_tracker.rs similarity index 100% rename from radix-engine-tests/tests/blueprints/transaction_tracker.rs rename to radix-engine-tests/tests/system/transaction_tracker.rs From 76585e4e3c2cedcea1ae713660d03103378287d6 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Thu, 12 Sep 2024 23:18:39 -0500 Subject: [PATCH 12/12] Fix compilation --- radix-engine-tests/tests/system/subintent_auth.rs | 14 +++++++------- radix-engine-tests/tests/system/subintent_leaks.rs | 10 +++++----- .../tests/system/subintent_txn_shape.rs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/radix-engine-tests/tests/system/subintent_auth.rs b/radix-engine-tests/tests/system/subintent_auth.rs index 0932787ed33..8597e7e4c9a 100644 --- a/radix-engine-tests/tests/system/subintent_auth.rs +++ b/radix-engine-tests/tests/system/subintent_auth.rs @@ -11,7 +11,7 @@ use radix_engine_interface::blueprints::transaction_processor::{ use radix_engine_interface::macros::dec; use radix_rust::btreeset; use radix_rust::prelude::{index_map_new, IndexMap}; -use radix_transactions::builder::{ManifestBuilder, ManifestV2Builder}; +use radix_transactions::builder::ManifestBuilder; use radix_transactions::model::{InstructionV1, ManifestIntent, TestTransaction}; use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; @@ -24,7 +24,7 @@ fn should_not_be_able_to_use_root_auth_in_subintent() { // Act let intents = vec![ { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .lock_standard_test_fee(account) .yield_to_child(ManifestIntent(0), ()) .build(); @@ -37,7 +37,7 @@ fn should_not_be_able_to_use_root_auth_in_subintent() { ) }, { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .withdraw_from_account(account, XRD, dec!(10)) .deposit_entire_worktop(account) .build(); @@ -78,7 +78,7 @@ fn should_be_able_to_use_separate_auth_in_subintent() { // Act let intents = vec![ { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .lock_standard_test_fee(account) .yield_to_child(ManifestIntent(0), ()) .build(); @@ -91,7 +91,7 @@ fn should_be_able_to_use_separate_auth_in_subintent() { ) }, { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .withdraw_from_account(account2, XRD, dec!(10)) .deposit_entire_worktop(account2) .build(); @@ -134,7 +134,7 @@ fn should_not_be_able_to_call_tx_processor_in_subintent() { // Act let intents = vec![ { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .lock_standard_test_fee(account) .yield_to_child(ManifestIntent(0), ()) .build(); @@ -149,7 +149,7 @@ fn should_not_be_able_to_call_tx_processor_in_subintent() { { let instructions: Vec = Vec::new(); let manifest_encoded_instructions = manifest_encode(&instructions).unwrap(); - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .call_function( TRANSACTION_PROCESSOR_PACKAGE, TRANSACTION_PROCESSOR_BLUEPRINT, diff --git a/radix-engine-tests/tests/system/subintent_leaks.rs b/radix-engine-tests/tests/system/subintent_leaks.rs index 06ef943fe55..460252df38d 100644 --- a/radix-engine-tests/tests/system/subintent_leaks.rs +++ b/radix-engine-tests/tests/system/subintent_leaks.rs @@ -5,8 +5,8 @@ use radix_engine::errors::{ApplicationError, RuntimeError}; use radix_engine::transaction::ExecutionConfig; use radix_engine_interface::macros::dec; use radix_rust::btreeset; -use radix_transactions::builder::ManifestV2Builder; use radix_transactions::model::{ManifestIntent, TestTransaction}; +use radix_transactions::prelude::ManifestBuilder; use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; #[test] @@ -19,7 +19,7 @@ fn bucket_leak_in_subintent_should_fail() { // Act let intents = vec![ { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .lock_standard_test_fee(account) .yield_to_child(ManifestIntent(0), ()) .build(); @@ -32,7 +32,7 @@ fn bucket_leak_in_subintent_should_fail() { ) }, { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .withdraw_from_account(account2, XRD, dec!(10)) .build(); @@ -74,7 +74,7 @@ fn proofs_in_subintent_should_autodrop() { // Act let intents = vec![ { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .lock_standard_test_fee(account) .yield_to_child(ManifestIntent(0), ()) .build(); @@ -87,7 +87,7 @@ fn proofs_in_subintent_should_autodrop() { ) }, { - let manifest = ManifestV2Builder::new_v2() + let manifest = ManifestBuilder::new_v2() .create_proof_from_account_of_amount(account2, XRD, dec!(10)) .build(); diff --git a/radix-engine-tests/tests/system/subintent_txn_shape.rs b/radix-engine-tests/tests/system/subintent_txn_shape.rs index 5cce33e7217..a9c5b33ef89 100644 --- a/radix-engine-tests/tests/system/subintent_txn_shape.rs +++ b/radix-engine-tests/tests/system/subintent_txn_shape.rs @@ -1,8 +1,8 @@ use radix_common::prelude::{FromPublicKey, NonFungibleGlobalId}; use radix_engine::transaction::ExecutionConfig; use radix_rust::btreeset; -use radix_transactions::builder::ManifestV2Builder; use radix_transactions::model::{ManifestIntent, TestTransaction}; +use radix_transactions::prelude::ManifestBuilder; use scrypto_test::ledger_simulator::LedgerSimulatorBuilder; #[test] @@ -29,7 +29,7 @@ fn test_subintent_txn_shape(children: Vec>) { let mut intents = vec![]; for (index, intent_children) in children.into_iter().enumerate() { - let mut builder = ManifestV2Builder::new_v2(); + let mut builder = ManifestBuilder::new_v2(); if index == 0 { builder = builder.lock_standard_test_fee(account); }