From 1e6f577d8fefae01c0910ab2a6a9c36e5a28365b Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 19 Jan 2025 01:28:49 +0000 Subject: [PATCH 1/8] chore: include latest_assume_valid_target.rs when merging master --- devtools/git/merge-master.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devtools/git/merge-master.sh b/devtools/git/merge-master.sh index 750de13bbd..a46f424a11 100755 --- a/devtools/git/merge-master.sh +++ b/devtools/git/merge-master.sh @@ -3,4 +3,8 @@ set -eu git merge --no-ff --no-commit -s ours master -git checkout master -- CHANGELOG.md src/main.rs resource/specs/testnet.toml resource/specs/mainnet.toml resource/specs/mainnet.toml.asc .github/workflows/package.yaml util/constant/src/default_assume_valid_target.rs +git checkout master -- \ + CHANGELOG.md src/main.rs resource/specs/testnet.toml resource/specs/mainnet.toml \ + resource/specs/mainnet.toml.asc .github/workflows/package.yaml \ + util/constant/src/default_assume_valid_target.rs \ + util/constant/src/latest_assume_valid_target.rs From b0a8075d2efea9842487ab51b39c3de09de4b9a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 18:02:29 +0000 Subject: [PATCH 2/8] chore(deps): bump hickory-proto from 0.24.2 to 0.24.3 Bumps [hickory-proto](https://github.com/hickory-dns/hickory-dns) from 0.24.2 to 0.24.3. - [Release notes](https://github.com/hickory-dns/hickory-dns/releases) - [Changelog](https://github.com/hickory-dns/hickory-dns/blob/v0.24.3/CHANGELOG.md) - [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.2...v0.24.3) --- updated-dependencies: - dependency-name: hickory-proto dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 794419adca..590a70873a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3088,9 +3088,9 @@ dependencies = [ [[package]] name = "hickory-proto" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +checksum = "2ad3d6d98c648ed628df039541a5577bee1a7c83e9e16fe3dbedeea4cdfeb971" dependencies = [ "async-trait", "cfg-if", @@ -3822,7 +3822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From d0baaf38bd60375003bf1d8334f227a304deb379 Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Mon, 20 Jan 2025 05:34:26 +0000 Subject: [PATCH 3/8] refactor: ckb-script package overhaul This commit revisits ckb-script package, it re-architects the whole data flow within ckb-script, to make it simpler and more maintainable --- script/src/lib.rs | 3 +- script/src/scheduler.rs | 224 +++--- script/src/syscalls/close.rs | 13 +- script/src/syscalls/current_cycles.rs | 12 +- script/src/syscalls/debugger.rs | 10 +- script/src/syscalls/exec.rs | 61 +- script/src/syscalls/exec_v2.rs | 13 +- script/src/syscalls/generator.rs | 62 ++ script/src/syscalls/inherited_fd.rs | 13 +- script/src/syscalls/load_block_extension.rs | 31 +- script/src/syscalls/load_cell.rs | 53 +- script/src/syscalls/load_cell_data.rs | 12 +- script/src/syscalls/load_header.rs | 51 +- script/src/syscalls/load_input.rs | 23 +- script/src/syscalls/load_script.rs | 8 +- script/src/syscalls/load_script_hash.rs | 8 +- script/src/syscalls/load_tx.rs | 7 +- script/src/syscalls/load_witness.rs | 35 +- script/src/syscalls/mod.rs | 2 + script/src/syscalls/pipe.rs | 13 +- script/src/syscalls/process_id.rs | 6 +- script/src/syscalls/read.rs | 13 +- script/src/syscalls/spawn.rs | 16 +- script/src/syscalls/tests/utils.rs | 61 +- .../syscalls/tests/vm_latest/syscalls_1.rs | 455 +++++------ .../syscalls/tests/vm_latest/syscalls_2.rs | 23 +- script/src/syscalls/wait.rs | 13 +- script/src/syscalls/write.rs | 13 +- script/src/types.rs | 535 +++++++++++-- script/src/verify.rs | 726 ++---------------- .../tests/ckb_latest/features_since_v2019.rs | 2 +- .../tests/ckb_latest/features_since_v2021.rs | 14 +- .../tests/ckb_latest/features_since_v2023.rs | 3 +- script/src/verify/tests/utils.rs | 6 +- 34 files changed, 1291 insertions(+), 1249 deletions(-) create mode 100644 script/src/syscalls/generator.rs diff --git a/script/src/lib.rs b/script/src/lib.rs index 3158a281b4..2016aeb3b1 100644 --- a/script/src/lib.rs +++ b/script/src/lib.rs @@ -10,9 +10,10 @@ mod verify_env; pub use crate::error::{ScriptError, TransactionScriptError}; pub use crate::scheduler::{Scheduler, ROOT_VM_ID}; +pub use crate::syscalls::generator::generate_ckb_syscalls; pub use crate::types::{ ChunkCommand, CoreMachine, DataPieceId, RunMode, ScriptGroup, ScriptGroupType, ScriptVersion, TransactionState, TxData, VerifyResult, VmIsa, VmState, VmVersion, }; -pub use crate::verify::{TransactionScriptsSyscallsGenerator, TransactionScriptsVerifier}; +pub use crate::verify::TransactionScriptsVerifier; pub use crate::verify_env::TxVerifyEnv; diff --git a/script/src/scheduler.rs b/script/src/scheduler.rs index b83b1b5b9d..8e28ac2425 100644 --- a/script/src/scheduler.rs +++ b/script/src/scheduler.rs @@ -1,15 +1,13 @@ use crate::cost_model::transferred_byte_cycles; use crate::syscalls::{ - EXEC_LOAD_ELF_V2_CYCLES_BASE, INVALID_FD, MAX_FDS_CREATED, MAX_VMS_SPAWNED, OTHER_END_CLOSED, - SPAWN_EXTRA_CYCLES_BASE, SUCCESS, WAIT_FAILURE, + generator::generate_ckb_syscalls, EXEC_LOAD_ELF_V2_CYCLES_BASE, INVALID_FD, MAX_FDS_CREATED, + MAX_VMS_SPAWNED, OTHER_END_CLOSED, SPAWN_EXTRA_CYCLES_BASE, SUCCESS, WAIT_FAILURE, }; -use crate::types::MachineContext; -use crate::verify::TransactionScriptsSyscallsGenerator; -use crate::ScriptVersion; use crate::types::{ - CoreMachineType, DataLocation, DataPieceId, Fd, FdArgs, FullSuspendedState, Machine, Message, - ReadState, RunMode, TxData, VmId, VmState, WriteState, FIRST_FD_SLOT, FIRST_VM_ID, + CoreMachineType, DataLocation, DataPieceId, DebugContext, Fd, FdArgs, FullSuspendedState, + Machine, Message, ReadState, RunMode, SgData, VmContext, VmData, VmId, VmState, WriteState, + FIRST_FD_SLOT, FIRST_VM_ID, }; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_types::core::Cycle; @@ -44,23 +42,45 @@ pub const MAX_FDS: u64 = 64; /// of the core for IO operations. pub struct Scheduler
where - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + DL: CellDataProvider, { - /// Context data for current running transaction & script. - pub tx_data: TxData
, - /// In fact, Scheduler here has the potential to totally replace - /// TransactionScriptsVerifier, nonetheless much of current syscall - /// implementation is strictly tied to TransactionScriptsVerifier, we - /// are using it here to save some extra code. - pub script_version: ScriptVersion, - /// Generate system calls. - pub syscalls_generator: TransactionScriptsSyscallsGenerator
, - - /// Total cycles. - pub total_cycles: Cycle, - /// Current iteration cycles. This value is periodically added to - /// total_cycles and cleared - pub current_iteration_cycles: Cycle, + /// Immutable context data for current running transaction & script. + pub sg_data: Arc>, + + /// Mutable context data used by current scheduler + pub debug_context: DebugContext, + + /// Total cycles. When a scheduler executes, there are 3 variables + /// that might all contain charged cycles: +total_cycles+, + /// +iteration_cycles+ and +machine.cycles()+ from the current + /// executing virtual machine. At any given time, the sum of all 3 + /// variables here, represent the total consumed cycles by the current + /// scheduler. + /// But there are also exceptions: at certain period of time, the cycles + /// stored in `machine.cycles()` are moved over to +iteration_cycles+, + /// the cycles stored in +iteration_cycles+ would also be moved over to + /// +total_cycles+: + /// + /// * The current running virtual machine would contain consumed + /// cycles in its own machine.cycles() structure. + /// * +iteration_cycles+ holds the current consumed cycles each time + /// we executed a virtual machine(also named an iteration). It will + /// always be zero before each iteration(i.e., before each VM starts + /// execution). When a virtual machine finishes execution, the cycles + /// stored in `machine.cycles()` will be moved over to +iteration_cycles+. + /// `machine.cycles()` will then be reset to zero. + /// * Processing messages in the message box would alao charge cycles + /// for operations, such as suspending/resuming VMs, transferring data + /// etc. Those cycles were added to +iteration_cycles+ directly. When all + /// postprocessing work is completed, the cycles consumed in + /// +iteration_cycles+ will then be moved to +total_cycles+. + /// +iteration_cycles+ will then be reset to zero. + /// + /// One can consider that +total_cycles+ contains the total cycles + /// consumed in current scheduler, when the scheduler is not busy executing. + pub total_cycles: Arc>, + /// Iteration cycles, see +total_cycles+ on its usage + pub iteration_cycles: Cycle, /// Next vm id used by spawn. pub next_vm_id: VmId, /// Next fd used by pipe. @@ -72,7 +92,7 @@ where /// Verify the VM's inherited fd list. pub inherited_fd: BTreeMap>, /// Instantiated vms. - pub instantiated: BTreeMap, Machine)>, + pub instantiated: BTreeMap, Machine)>, /// Suspended vms. pub suspended: BTreeMap>, /// Terminated vms. @@ -88,18 +108,12 @@ where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { /// Create a new scheduler from empty state - pub fn new( - tx_data: TxData
, - script_version: ScriptVersion, - syscalls_generator: TransactionScriptsSyscallsGenerator
, - ) -> Self { - let message_box = Arc::clone(&syscalls_generator.message_box); + pub fn new(sg_data: SgData
, debug_context: DebugContext) -> Self { Self { - tx_data, - script_version, - syscalls_generator, - total_cycles: 0, - current_iteration_cycles: 0, + sg_data: Arc::new(sg_data), + debug_context, + total_cycles: Arc::new(Mutex::new(0)), + iteration_cycles: 0, next_vm_id: FIRST_VM_ID, next_fd_slot: FIRST_FD_SLOT, states: BTreeMap::default(), @@ -107,20 +121,20 @@ where inherited_fd: BTreeMap::default(), instantiated: BTreeMap::default(), suspended: BTreeMap::default(), - message_box, + message_box: Arc::new(Mutex::new(Vec::new())), terminated_vms: BTreeMap::default(), } } /// Return total cycles. pub fn consumed_cycles(&self) -> Cycle { - self.total_cycles + *self.total_cycles.lock().expect("mutex") } /// Add cycles to total cycles. - pub fn consumed_cycles_add(&mut self, cycles: Cycle) -> Result<(), Error> { - self.total_cycles = self - .total_cycles + pub fn consume_cycles(&mut self, cycles: Cycle) -> Result<(), Error> { + let mut total_cycles = self.total_cycles.lock().expect("mutex"); + *total_cycles = (*total_cycles) .checked_add(cycles) .ok_or(Error::CyclesExceeded)?; Ok(()) @@ -128,18 +142,15 @@ where /// Resume a previously suspended scheduler state pub fn resume( - tx_data: TxData
, - script_version: ScriptVersion, - syscalls_generator: TransactionScriptsSyscallsGenerator
, + sg_data: SgData
, + debug_context: DebugContext, full: FullSuspendedState, ) -> Self { - let message_box = Arc::clone(&syscalls_generator.message_box); let mut scheduler = Self { - tx_data, - script_version, - syscalls_generator, - total_cycles: full.total_cycles, - current_iteration_cycles: 0, + sg_data: Arc::new(sg_data), + debug_context, + total_cycles: Arc::new(Mutex::new(full.total_cycles)), + iteration_cycles: 0, next_vm_id: full.next_vm_id, next_fd_slot: full.next_fd_slot, states: full @@ -155,12 +166,16 @@ where .into_iter() .map(|(id, _, snapshot)| (id, snapshot)) .collect(), - message_box, + message_box: Arc::new(Mutex::new(Vec::new())), terminated_vms: full.terminated_vms.into_iter().collect(), }; scheduler .ensure_vms_instantiated(&full.instantiated_ids) .unwrap(); + // NOTE: suspending/resuming a scheduler is part of CKB's implementation + // details. It is not part of execution consensue. We should not charge + // cycles for them. + scheduler.iteration_cycles = 0; scheduler } @@ -180,7 +195,11 @@ where vms.push((id, state, snapshot)); } Ok(FullSuspendedState { - total_cycles: self.total_cycles, + // NOTE: suspending a scheduler is actually part of CKB's + // internal execution logic, it does not belong to VM execution + // consensus. We are not charging cycles for suspending + // a VM in the process of suspending the whole scheduler. + total_cycles: *self.total_cycles.lock().expect("mutex"), next_vm_id: self.next_vm_id, next_fd_slot: self.next_fd_slot, vms, @@ -207,10 +226,11 @@ where pub fn run(&mut self, mode: RunMode) -> Result<(i8, Cycle), Error> { if self.states.is_empty() { // Booting phase, we will need to initialize the first VM. + let program_id = self.sg_data.program_data_piece_id.clone(); assert_eq!( self.boot_vm( &DataLocation { - data_piece_id: DataPieceId::Program, + data_piece_id: program_id, offset: 0, length: u64::MAX, }, @@ -227,26 +247,24 @@ where }; while self.states[&ROOT_VM_ID] != VmState::Terminated { - self.current_iteration_cycles = 0; + assert_eq!(self.iteration_cycles, 0); let iterate_return = self.iterate(pause.clone(), limit_cycles); - self.consumed_cycles_add(self.current_iteration_cycles)?; + self.consume_cycles(self.iteration_cycles)?; limit_cycles = limit_cycles - .checked_sub(self.current_iteration_cycles) + .checked_sub(self.iteration_cycles) .ok_or(Error::CyclesExceeded)?; + // Clear iteration cycles intentionally after each run + self.iteration_cycles = 0; iterate_return?; } // At this point, root VM cannot be suspended let root_vm = &self.instantiated[&ROOT_VM_ID]; - Ok((root_vm.1.machine.exit_code(), self.total_cycles)) + Ok((root_vm.1.machine.exit_code(), self.consumed_cycles())) } /// Returns the machine that needs to be executed in the current iterate. - pub fn iterate_prepare_machine( - &mut self, - pause: Pause, - limit_cycles: Cycle, - ) -> Result<(u64, &mut Machine), Error> { + pub fn iterate_prepare_machine(&mut self) -> Result<(u64, &mut Machine), Error> { // Process all pending VM reads & writes. self.process_io()?; // Find a runnable VM that has the largest ID. @@ -260,11 +278,7 @@ where let vm_id_to_run = vm_id_to_run.ok_or_else(|| { Error::Unexpected("A deadlock situation has been reached!".to_string()) })?; - let total_cycles = self.total_cycles; - let (context, machine) = self.ensure_get_instantiated(&vm_id_to_run)?; - context.set_base_cycles(total_cycles); - machine.set_max_cycles(limit_cycles); - machine.machine.set_pause(pause); + let (_context, machine) = self.ensure_get_instantiated(&vm_id_to_run)?; Ok((vm_id_to_run, machine)) } @@ -273,12 +287,7 @@ where &mut self, vm_id_to_run: u64, result: Result, - cycles: u64, ) -> Result<(), Error> { - self.current_iteration_cycles = self - .current_iteration_cycles - .checked_add(cycles) - .ok_or(Error::CyclesOverflow)?; // Process message box, update VM states accordingly self.process_message_box()?; assert!(self.message_box.lock().expect("lock").is_empty()); @@ -336,11 +345,23 @@ where // Here both pause signal and limit_cycles are provided so as to simplify // branches. fn iterate(&mut self, pause: Pause, limit_cycles: Cycle) -> Result<(), Error> { - let (id, vm) = self.iterate_prepare_machine(pause, limit_cycles)?; - let result = vm.run(); - let cycles = vm.machine.cycles(); - vm.machine.set_cycles(0); - self.iterate_process_results(id, result, cycles) + // Execute the VM for real, consumed cycles in the virtual machine is + // moved over to +iteration_cycles+, then we reset virtual machine's own + // cycle count to zero. + let (id, result, cycles) = { + let (id, vm) = self.iterate_prepare_machine()?; + vm.set_max_cycles(limit_cycles); + vm.machine.set_pause(pause); + let result = vm.run(); + let cycles = vm.machine.cycles(); + vm.machine.set_cycles(0); + (id, result, cycles) + }; + self.iteration_cycles = self + .iteration_cycles + .checked_add(cycles) + .ok_or(Error::CyclesExceeded)?; + self.iterate_process_results(id, result) } fn process_message_box(&mut self) -> Result<(), Error> { @@ -358,7 +379,7 @@ where let old_cycles = old_machine.machine.cycles(); let max_cycles = old_machine.machine.max_cycles(); let program = { - let mut sc = old_context.snapshot2_context().lock().expect("lock"); + let mut sc = old_context.snapshot2_context.lock().expect("lock"); sc.load_data( &args.location.data_piece_id, args.location.offset, @@ -731,7 +752,7 @@ where fn ensure_get_instantiated( &mut self, id: &VmId, - ) -> Result<&mut (MachineContext
, Machine), Error> { + ) -> Result<&mut (VmContext
, Machine), Error> { self.ensure_vms_instantiated(&[*id])?; self.instantiated .get_mut(id) @@ -744,13 +765,13 @@ where return Err(Error::Unexpected(format!("VM {:?} is not suspended!", id))); } let snapshot = &self.suspended[id]; - self.current_iteration_cycles = self - .current_iteration_cycles + self.iteration_cycles = self + .iteration_cycles .checked_add(SPAWN_EXTRA_CYCLES_BASE) .ok_or(Error::CyclesExceeded)?; let (context, mut machine) = self.create_dummy_vm(id)?; { - let mut sc = context.snapshot2_context().lock().expect("lock"); + let mut sc = context.snapshot2_context.lock().expect("lock"); sc.resume(&mut machine.machine, snapshot)?; } self.instantiated.insert(*id, (context, machine)); @@ -766,8 +787,8 @@ where id ))); } - self.current_iteration_cycles = self - .current_iteration_cycles + self.iteration_cycles = self + .iteration_cycles .checked_add(SPAWN_EXTRA_CYCLES_BASE) .ok_or(Error::CyclesExceeded)?; let (context, machine) = self @@ -775,7 +796,7 @@ where .get_mut(id) .ok_or_else(|| Error::Unexpected("Unable to find VM Id".to_string()))?; let snapshot = { - let sc = context.snapshot2_context().lock().expect("lock"); + let sc = context.snapshot2_context.lock().expect("lock"); sc.make_snapshot(&mut machine.machine)? }; self.suspended.insert(*id, snapshot); @@ -793,7 +814,7 @@ where self.next_vm_id += 1; let (context, mut machine) = self.create_dummy_vm(&id)?; let (program, _) = { - let mut sc = context.snapshot2_context().lock().expect("lock"); + let mut sc = context.snapshot2_context.lock().expect("lock"); sc.load_data(&location.data_piece_id, location.offset, location.length)? }; self.load_vm_program(&context, &mut machine, location, program, args)?; @@ -807,6 +828,7 @@ where .key(); self.suspend_vm(&id)?; } + self.instantiated.insert(id, (context, machine)); self.states.insert(id, VmState::Runnable); @@ -816,7 +838,7 @@ where // Load the program into an empty vm. fn load_vm_program( &mut self, - context: &MachineContext
, + context: &VmContext
, machine: &mut Machine, location: &DataLocation, program: Bytes, @@ -831,7 +853,7 @@ where } None => machine.load_program_with_metadata(&program, &metadata, vec![].into_iter())?, }; - let mut sc = context.snapshot2_context().lock().expect("lock"); + let mut sc = context.snapshot2_context.lock().expect("lock"); sc.mark_program( &mut machine.machine, &metadata, @@ -845,35 +867,33 @@ where } // Create a new VM instance with syscalls attached - fn create_dummy_vm(&self, id: &VmId) -> Result<(MachineContext
, Machine), Error> { + fn create_dummy_vm(&self, id: &VmId) -> Result<(VmContext
, Machine), Error> { // The code here looks slightly weird, since I don't want to copy over all syscall // impls here again. Ideally, this scheduler package should be merged with ckb-script, // or simply replace ckb-script. That way, the quirks here will be eliminated. - let version = self.script_version; + let version = &self.sg_data.script_version; let core_machine = CoreMachineType::new( version.vm_isa(), version.vm_version(), // We will update max_cycles for each machine when it gets a chance to run u64::MAX, ); - let snapshot2_context = Arc::new(Mutex::new(Snapshot2Context::new(self.tx_data.clone()))); - let mut syscalls_generator = self.syscalls_generator.clone(); - syscalls_generator.vm_id = *id; - let mut machine_context = MachineContext::new(self.tx_data.clone()); - machine_context.base_cycles = Arc::clone(&self.syscalls_generator.base_cycles); - machine_context.snapshot2_context = Arc::clone(&snapshot2_context); + let vm_data = Arc::new(VmData { + sg_data: Arc::clone(&self.sg_data), + vm_id: *id, + }); + let vm_context = VmContext { + base_cycles: Arc::clone(&self.total_cycles), + message_box: Arc::clone(&self.message_box), + snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(&vm_data)))), + }; let machine_builder = DefaultMachineBuilder::new(core_machine) .instruction_cycle_func(Box::new(estimate_cycles)); - let machine_builder = syscalls_generator - .generate_syscalls( - version, - &self.tx_data.script_group, - Arc::clone(&snapshot2_context), - ) + let machine_builder = generate_ckb_syscalls(&vm_data, &vm_context, &self.debug_context) .into_iter() .fold(machine_builder, |builder, syscall| builder.syscall(syscall)); let default_machine = machine_builder.build(); - Ok((machine_context, Machine::new(default_machine))) + Ok((vm_context, Machine::new(default_machine))) } } diff --git a/script/src/syscalls/close.rs b/script/src/syscalls/close.rs index 1e359095be..88c0124326 100644 --- a/script/src/syscalls/close.rs +++ b/script/src/syscalls/close.rs @@ -1,5 +1,6 @@ use crate::syscalls::{CLOSE, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Fd, Message, VmId}; +use crate::types::{Fd, Message, VmContext, VmData, VmId}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -13,8 +14,14 @@ pub struct Close { } impl Close { - pub fn new(id: VmId, message_box: Arc>>) -> Self { - Self { id, message_box } + pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/syscalls/current_cycles.rs b/script/src/syscalls/current_cycles.rs index a791cef7a4..d62684066a 100644 --- a/script/src/syscalls/current_cycles.rs +++ b/script/src/syscalls/current_cycles.rs @@ -1,4 +1,5 @@ -use crate::syscalls::CURRENT_CYCLES; +use crate::{syscalls::CURRENT_CYCLES, types::VmContext}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -11,8 +12,13 @@ pub struct CurrentCycles { } impl CurrentCycles { - pub fn new(base: Arc>) -> Self { - Self { base } + pub fn new
(vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + base: Arc::clone(&vm_context.base_cycles), + } } } diff --git a/script/src/syscalls/debugger.rs b/script/src/syscalls/debugger.rs index 1b2158a7e7..88e21ad0a2 100644 --- a/script/src/syscalls/debugger.rs +++ b/script/src/syscalls/debugger.rs @@ -1,10 +1,11 @@ -use crate::types::DebugPrinter; +use crate::types::{DebugContext, DebugPrinter, VmData}; use crate::{cost_model::transferred_byte_cycles, syscalls::DEBUG_PRINT_SYSCALL_NUMBER}; use ckb_types::packed::Byte32; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Memory, Register, SupportMachine, Syscalls, }; +use std::sync::Arc; pub struct Debugger { hash: Byte32, @@ -12,8 +13,11 @@ pub struct Debugger { } impl Debugger { - pub fn new(hash: Byte32, printer: DebugPrinter) -> Debugger { - Debugger { hash, printer } + pub fn new
(vm_data: &Arc>, debug_context: &DebugContext) -> Debugger { + Debugger { + hash: vm_data.current_script_hash(), + printer: Arc::clone(&debug_context.debug_printer), + } } } diff --git a/script/src/syscalls/exec.rs b/script/src/syscalls/exec.rs index 1e934fddf8..56b7fabc36 100644 --- a/script/src/syscalls/exec.rs +++ b/script/src/syscalls/exec.rs @@ -3,9 +3,9 @@ use crate::syscalls::{ Place, Source, SourceEntry, EXEC, INDEX_OUT_OF_BOUND, MAX_ARGV_LENGTH, SLICE_OUT_OF_BOUND, WRONG_FORMAT, }; -use crate::types::Indices; +use crate::types::VmData; use ckb_traits::CellDataProvider; -use ckb_types::core::cell::{CellMeta, ResolvedTransaction}; +use ckb_types::core::cell::CellMeta; use ckb_types::core::error::ARGV_TOO_LONG_TEXT; use ckb_types::packed::{Bytes as PackedBytes, BytesVec}; use ckb_vm::memory::load_c_string_byte_by_byte; @@ -19,58 +19,46 @@ use std::sync::Arc; #[derive(Debug)] pub struct Exec
{ - data_loader: DL, - rtx: Arc, - outputs: Arc>, - group_inputs: Indices, - group_outputs: Indices, + vm_data: Arc>, } impl Exec
{ - pub fn new( - data_loader: DL, - rtx: Arc, - outputs: Arc>, - group_inputs: Indices, - group_outputs: Indices, - ) -> Exec
{ + pub fn new(vm_data: &Arc>) -> Exec
{ Exec { - data_loader, - rtx, - outputs, - group_inputs, - group_outputs, + vm_data: Arc::clone(vm_data), } } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.rtx.resolved_inputs + &self.vm_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.rtx.resolved_cell_deps + &self.vm_data.rtx().resolved_cell_deps } #[inline] fn witnesses(&self) -> BytesVec { - self.rtx.transaction.witnesses() + self.vm_data.rtx().transaction.witnesses() } fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { let cell_opt = match source { Source::Transaction(SourceEntry::Input) => self.resolved_inputs().get(index), - Source::Transaction(SourceEntry::Output) => self.outputs.get(index), + Source::Transaction(SourceEntry::Output) => self.vm_data.outputs().get(index), Source::Transaction(SourceEntry::CellDep) => self.resolved_cell_deps().get(index), Source::Group(SourceEntry::Input) => self - .group_inputs + .vm_data + .group_inputs() .get(index) .and_then(|actual_index| self.resolved_inputs().get(*actual_index)), Source::Group(SourceEntry::Output) => self - .group_outputs + .vm_data + .group_outputs() .get(index) - .and_then(|actual_index| self.outputs.get(*actual_index)), + .and_then(|actual_index| self.vm_data.outputs().get(*actual_index)), Source::Transaction(SourceEntry::HeaderDep) | Source::Group(SourceEntry::CellDep) | Source::Group(SourceEntry::HeaderDep) => { @@ -84,11 +72,13 @@ impl Exec
{ fn fetch_witness(&self, source: Source, index: usize) -> Result { let witness_opt = match source { Source::Group(SourceEntry::Input) => self - .group_inputs + .vm_data + .group_inputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), Source::Group(SourceEntry::Output) => self - .group_outputs + .vm_data + .group_outputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), Source::Transaction(SourceEntry::Input) => self.witnesses().get(index), @@ -126,12 +116,15 @@ impl Syscalls for return Ok(true); } let cell = cell.unwrap(); - self.data_loader.load_cell_data(cell).ok_or_else(|| { - VMError::Unexpected(format!( - "Unexpected load_cell_data failed {}", - cell.out_point, - )) - })? + self.vm_data + .data_loader() + .load_cell_data(cell) + .ok_or_else(|| { + VMError::Unexpected(format!( + "Unexpected load_cell_data failed {}", + cell.out_point, + )) + })? } Place::Witness => { let witness = self.fetch_witness(source, index as usize); diff --git a/script/src/syscalls/exec_v2.rs b/script/src/syscalls/exec_v2.rs index ab5b2facce..570c39a441 100644 --- a/script/src/syscalls/exec_v2.rs +++ b/script/src/syscalls/exec_v2.rs @@ -1,5 +1,6 @@ use crate::syscalls::{EXEC, INDEX_OUT_OF_BOUND}; -use crate::types::{DataLocation, DataPieceId, ExecV2Args, Message, VmId}; +use crate::types::{DataLocation, DataPieceId, ExecV2Args, Message, VmContext, VmData, VmId}; +use ckb_traits::CellDataProvider; use ckb_vm::{ registers::{A0, A1, A2, A3, A4, A5, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -12,8 +13,14 @@ pub struct ExecV2 { } impl ExecV2 { - pub fn new(id: VmId, message_box: Arc>>) -> ExecV2 { - ExecV2 { id, message_box } + pub fn new( + vm_data: &Arc>, + vm_context: &VmContext
, + ) -> ExecV2 { + ExecV2 { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/syscalls/generator.rs b/script/src/syscalls/generator.rs new file mode 100644 index 0000000000..38a3c17cbf --- /dev/null +++ b/script/src/syscalls/generator.rs @@ -0,0 +1,62 @@ +use crate::{ + syscalls::{ + Close, CurrentCycles, Debugger, Exec, ExecV2, InheritedFd, LoadBlockExtension, LoadCell, + LoadCellData, LoadHeader, LoadInput, LoadScript, LoadScriptHash, LoadTx, LoadWitness, Pipe, + ProcessID, Read, Spawn, VMVersion, Wait, Write, + }, + types::{CoreMachine, DebugContext, ScriptVersion, VmContext, VmData}, +}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; +use ckb_vm::Syscalls; +use std::sync::Arc; + +/// Generate RISC-V syscalls in CKB environment +pub fn generate_ckb_syscalls
( + vm_data: &Arc>, + vm_context: &VmContext
, + debug_context: &DebugContext, +) -> Vec)>> +where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, +{ + let mut syscalls: Vec)>> = vec![ + Box::new(LoadScriptHash::new(vm_data)), + Box::new(LoadTx::new(vm_data)), + Box::new(LoadCell::new(vm_data)), + Box::new(LoadInput::new(vm_data)), + Box::new(LoadHeader::new(vm_data)), + Box::new(LoadWitness::new(vm_data)), + Box::new(LoadScript::new(vm_data)), + Box::new(LoadCellData::new(vm_context)), + Box::new(Debugger::new(vm_data, debug_context)), + ]; + let script_version = &vm_data.sg_data.script_version; + if script_version >= &ScriptVersion::V1 { + syscalls.append(&mut vec![ + Box::new(VMVersion::new()), + Box::new(CurrentCycles::new(vm_context)), + ]); + } + if script_version == &ScriptVersion::V1 { + syscalls.push(Box::new(Exec::new(vm_data))); + } + if script_version >= &ScriptVersion::V2 { + syscalls.append(&mut vec![ + Box::new(ExecV2::new(vm_data, vm_context)), + Box::new(LoadBlockExtension::new(vm_data)), + Box::new(Spawn::new(vm_data, vm_context)), + Box::new(ProcessID::new(vm_data)), + Box::new(Pipe::new(vm_data, vm_context)), + Box::new(Wait::new(vm_data, vm_context)), + Box::new(Write::new(vm_data, vm_context)), + Box::new(Read::new(vm_data, vm_context)), + Box::new(InheritedFd::new(vm_data, vm_context)), + Box::new(Close::new(vm_data, vm_context)), + ]); + } + #[cfg(test)] + syscalls.push(Box::new(crate::syscalls::Pause::new(Arc::clone( + &debug_context.skip_pause, + )))); + syscalls +} diff --git a/script/src/syscalls/inherited_fd.rs b/script/src/syscalls/inherited_fd.rs index 4d041a0ba1..489a1b5101 100644 --- a/script/src/syscalls/inherited_fd.rs +++ b/script/src/syscalls/inherited_fd.rs @@ -1,5 +1,6 @@ use crate::syscalls::{INHERITED_FD, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Fd, FdArgs, Message, VmId}; +use crate::types::{Fd, FdArgs, Message, VmContext, VmData, VmId}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -13,8 +14,14 @@ pub struct InheritedFd { } impl InheritedFd { - pub fn new(id: VmId, message_box: Arc>>) -> Self { - Self { id, message_box } + pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/syscalls/load_block_extension.rs b/script/src/syscalls/load_block_extension.rs index 061bb7c4c8..81d7de6286 100644 --- a/script/src/syscalls/load_block_extension.rs +++ b/script/src/syscalls/load_block_extension.rs @@ -1,13 +1,12 @@ -use crate::types::Indices; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ utils::store_data, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_BLOCK_EXTENSION, SUCCESS, }, + types::VmData, }; use ckb_traits::ExtensionProvider; -use ckb_types::core::cell::ResolvedTransaction; use ckb_types::{ core::cell::CellMeta, packed::{self, Byte32Vec}, @@ -20,37 +19,29 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadBlockExtension
{ - data_loader: DL, - rtx: Arc, - group_inputs: Indices, + vm_data: Arc>, } impl LoadBlockExtension
{ - pub fn new( - data_loader: DL, - rtx: Arc, - group_inputs: Indices, - ) -> LoadBlockExtension
{ + pub fn new(vm_data: &Arc>) -> LoadBlockExtension
{ LoadBlockExtension { - data_loader, - rtx, - group_inputs, + vm_data: Arc::clone(vm_data), } } #[inline] fn header_deps(&self) -> Byte32Vec { - self.rtx.transaction.header_deps() + self.vm_data.rtx().transaction.header_deps() } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.rtx.resolved_inputs + &self.vm_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.rtx.resolved_cell_deps + &self.vm_data.rtx().resolved_cell_deps } fn load_block_extension(&self, cell_meta: &CellMeta) -> Option { @@ -64,7 +55,7 @@ impl LoadBlockExtension
{ .into_iter() .any(|hash| &hash == block_hash) { - self.data_loader.get_block_extension(block_hash) + self.vm_data.data_loader().get_block_extension(block_hash) } else { None } @@ -88,12 +79,14 @@ impl LoadBlockExtension
{ .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|block_hash| { - self.data_loader + self.vm_data + .data_loader() .get_block_extension(&block_hash) .ok_or(ITEM_MISSING) }), Source::Group(SourceEntry::Input) => self - .group_inputs + .vm_data + .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|actual_index| { diff --git a/script/src/syscalls/load_cell.rs b/script/src/syscalls/load_cell.rs index 0dee77937f..4c71111bb1 100644 --- a/script/src/syscalls/load_cell.rs +++ b/script/src/syscalls/load_cell.rs @@ -1,18 +1,15 @@ -use crate::types::Indices; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ utils::store_data, CellField, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_CELL_BY_FIELD_SYSCALL_NUMBER, LOAD_CELL_SYSCALL_NUMBER, SUCCESS, }, + types::{TxData, VmData}, }; use byteorder::{LittleEndian, WriteBytesExt}; use ckb_traits::CellDataProvider; use ckb_types::{ - core::{ - cell::{CellMeta, ResolvedTransaction}, - Capacity, - }, + core::{cell::CellMeta, Capacity}, packed::CellOutput, prelude::*, }; @@ -23,38 +20,29 @@ use ckb_vm::{ use std::sync::Arc; pub struct LoadCell
{ - data_loader: DL, - rtx: Arc, - outputs: Arc>, - group_inputs: Indices, - group_outputs: Indices, + vm_data: Arc>, } impl LoadCell
{ - pub fn new( - data_loader: DL, - rtx: Arc, - outputs: Arc>, - group_inputs: Indices, - group_outputs: Indices, - ) -> LoadCell
{ + pub fn new(vm_data: &Arc>) -> LoadCell
{ LoadCell { - data_loader, - rtx, - outputs, - group_inputs, - group_outputs, + vm_data: Arc::clone(vm_data), } } + #[inline] + fn tx_data(&self) -> &TxData
{ + &self.vm_data.sg_data.tx_data + } + #[inline] fn resolved_inputs(&self) -> &Vec { - &self.rtx.resolved_inputs + &self.vm_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.rtx.resolved_cell_deps + &self.vm_data.rtx().resolved_cell_deps } fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { @@ -63,7 +51,7 @@ impl LoadCell
{ self.resolved_inputs().get(index).ok_or(INDEX_OUT_OF_BOUND) } Source::Transaction(SourceEntry::Output) => { - self.outputs.get(index).ok_or(INDEX_OUT_OF_BOUND) + self.tx_data().outputs.get(index).ok_or(INDEX_OUT_OF_BOUND) } Source::Transaction(SourceEntry::CellDep) => self .resolved_cell_deps() @@ -71,7 +59,8 @@ impl LoadCell
{ .ok_or(INDEX_OUT_OF_BOUND), Source::Transaction(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), Source::Group(SourceEntry::Input) => self - .group_inputs + .vm_data + .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|actual_index| { @@ -80,10 +69,16 @@ impl LoadCell
{ .ok_or(INDEX_OUT_OF_BOUND) }), Source::Group(SourceEntry::Output) => self - .group_outputs + .vm_data + .group_outputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) - .and_then(|actual_index| self.outputs.get(*actual_index).ok_or(INDEX_OUT_OF_BOUND)), + .and_then(|actual_index| { + self.tx_data() + .outputs + .get(*actual_index) + .ok_or(INDEX_OUT_OF_BOUND) + }), Source::Group(SourceEntry::CellDep) => Err(INDEX_OUT_OF_BOUND), Source::Group(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), } @@ -115,7 +110,7 @@ impl LoadCell
{ (SUCCESS, store_data(machine, &buffer)?) } CellField::DataHash => { - if let Some(bytes) = self.data_loader.load_cell_data_hash(cell) { + if let Some(bytes) = self.tx_data().data_loader.load_cell_data_hash(cell) { (SUCCESS, store_data(machine, &bytes.as_bytes())?) } else { (ITEM_MISSING, 0) diff --git a/script/src/syscalls/load_cell_data.rs b/script/src/syscalls/load_cell_data.rs index d9ee50be4b..c78f3dcec9 100644 --- a/script/src/syscalls/load_cell_data.rs +++ b/script/src/syscalls/load_cell_data.rs @@ -1,4 +1,4 @@ -use crate::types::{DataPieceId, TxData}; +use crate::types::{DataPieceId, VmContext, VmData}; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ @@ -20,17 +20,17 @@ pub struct LoadCellData
where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { - snapshot2_context: Arc>>>, + snapshot2_context: Arc>>>>, } impl
LoadCellData
where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { - pub fn new( - snapshot2_context: Arc>>>, - ) -> LoadCellData
{ - LoadCellData { snapshot2_context } + pub fn new(vm_context: &VmContext
) -> LoadCellData
{ + LoadCellData { + snapshot2_context: Arc::clone(&vm_context.snapshot2_context), + } } fn load_data(&self, machine: &mut Mac) -> Result<(), VMError> { diff --git a/script/src/syscalls/load_header.rs b/script/src/syscalls/load_header.rs index 41db861e7b..29df08cb04 100644 --- a/script/src/syscalls/load_header.rs +++ b/script/src/syscalls/load_header.rs @@ -1,4 +1,3 @@ -use crate::types::Indices; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ @@ -6,9 +5,9 @@ use crate::{ HeaderField, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_HEADER_BY_FIELD_SYSCALL_NUMBER, LOAD_HEADER_SYSCALL_NUMBER, SUCCESS, }, + types::VmData, }; use ckb_traits::HeaderProvider; -use ckb_types::core::cell::ResolvedTransaction; use ckb_types::{ core::{cell::CellMeta, HeaderView}, packed::Byte32Vec, @@ -22,41 +21,38 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadHeader
{ - data_loader: DL, - rtx: Arc, - // This can only be used for liner search - // header_deps: Byte32Vec, - // resolved_inputs: &'a [CellMeta], - // resolved_cell_deps: &'a [CellMeta], - group_inputs: Indices, + vm_data: Arc>, } impl LoadHeader
{ - pub fn new( - data_loader: DL, - rtx: Arc, - group_inputs: Indices, - ) -> LoadHeader
{ + pub fn new(vm_data: &Arc>) -> LoadHeader
{ LoadHeader { - data_loader, - rtx, - group_inputs, + vm_data: Arc::clone(vm_data), } } + // This can only be used for liner search + // header_deps: Byte32Vec, + // resolved_inputs: &'a [CellMeta], + // resolved_cell_deps: &'a [CellMeta], + #[inline] + fn group_inputs(&self) -> &[usize] { + self.vm_data.group_inputs() + } + #[inline] fn header_deps(&self) -> Byte32Vec { - self.rtx.transaction.header_deps() + self.vm_data.rtx().transaction.header_deps() } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.rtx.resolved_inputs + &self.vm_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.rtx.resolved_cell_deps + &self.vm_data.rtx().resolved_cell_deps } fn load_header(&self, cell_meta: &CellMeta) -> Option { @@ -70,7 +66,11 @@ impl LoadHeader
{ .into_iter() .any(|hash| &hash == block_hash) { - self.data_loader.get_header(block_hash) + self.vm_data + .sg_data + .tx_data + .data_loader + .get_header(block_hash) } else { None } @@ -94,10 +94,15 @@ impl LoadHeader
{ .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|block_hash| { - self.data_loader.get_header(&block_hash).ok_or(ITEM_MISSING) + self.vm_data + .sg_data + .tx_data + .data_loader + .get_header(&block_hash) + .ok_or(ITEM_MISSING) }), Source::Group(SourceEntry::Input) => self - .group_inputs + .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|actual_index| { diff --git a/script/src/syscalls/load_input.rs b/script/src/syscalls/load_input.rs index 8f19c0d6c9..a7331e8756 100644 --- a/script/src/syscalls/load_input.rs +++ b/script/src/syscalls/load_input.rs @@ -1,13 +1,12 @@ -use crate::types::Indices; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ utils::store_data, InputField, Source, SourceEntry, INDEX_OUT_OF_BOUND, LOAD_INPUT_BY_FIELD_SYSCALL_NUMBER, LOAD_INPUT_SYSCALL_NUMBER, SUCCESS, }, + types::VmData, }; use byteorder::{LittleEndian, WriteBytesExt}; -use ckb_types::core::cell::ResolvedTransaction; use ckb_types::{ packed::{CellInput, CellInputVec}, prelude::*, @@ -19,19 +18,20 @@ use ckb_vm::{ use std::sync::Arc; #[derive(Debug)] -pub struct LoadInput { - rtx: Arc, - group_inputs: Indices, +pub struct LoadInput
{ + vm_data: Arc>, } -impl LoadInput { - pub fn new(rtx: Arc, group_inputs: Indices) -> LoadInput { - LoadInput { rtx, group_inputs } +impl
LoadInput
{ + pub fn new(vm_data: &Arc>) -> Self { + LoadInput { + vm_data: Arc::clone(vm_data), + } } #[inline] fn inputs(&self) -> CellInputVec { - self.rtx.transaction.inputs() + self.vm_data.rtx().transaction.inputs() } fn fetch_input(&self, source: Source, index: usize) -> Result { @@ -43,7 +43,8 @@ impl LoadInput { Source::Transaction(SourceEntry::CellDep) => Err(INDEX_OUT_OF_BOUND), Source::Transaction(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), Source::Group(SourceEntry::Input) => self - .group_inputs + .vm_data + .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|actual_index| { @@ -87,7 +88,7 @@ impl LoadInput { } } -impl Syscalls for LoadInput { +impl Syscalls for LoadInput
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/load_script.rs b/script/src/syscalls/load_script.rs index 6fa9f1f8d9..0f8290498d 100644 --- a/script/src/syscalls/load_script.rs +++ b/script/src/syscalls/load_script.rs @@ -1,12 +1,14 @@ use crate::{ cost_model::transferred_byte_cycles, syscalls::{utils::store_data, LOAD_SCRIPT_SYSCALL_NUMBER, SUCCESS}, + types::VmData, }; use ckb_types::{packed::Script, prelude::*}; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; +use std::sync::Arc; #[derive(Debug)] pub struct LoadScript { @@ -14,8 +16,10 @@ pub struct LoadScript { } impl LoadScript { - pub fn new(script: Script) -> Self { - Self { script } + pub fn new
(vm_data: &Arc>) -> Self { + Self { + script: vm_data.sg_data.script_group.script.clone(), + } } } diff --git a/script/src/syscalls/load_script_hash.rs b/script/src/syscalls/load_script_hash.rs index d097cf0810..5dd0dfa401 100644 --- a/script/src/syscalls/load_script_hash.rs +++ b/script/src/syscalls/load_script_hash.rs @@ -1,12 +1,14 @@ use crate::{ cost_model::transferred_byte_cycles, syscalls::{utils::store_data, LOAD_SCRIPT_HASH_SYSCALL_NUMBER, SUCCESS}, + types::VmData, }; use ckb_types::packed::Byte32; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; +use std::sync::Arc; #[derive(Debug)] pub struct LoadScriptHash { @@ -14,8 +16,10 @@ pub struct LoadScriptHash { } impl LoadScriptHash { - pub fn new(hash: Byte32) -> LoadScriptHash { - LoadScriptHash { hash } + pub fn new
(vm_data: &Arc>) -> LoadScriptHash { + LoadScriptHash { + hash: vm_data.sg_data.script_group.script.calc_script_hash(), + } } } diff --git a/script/src/syscalls/load_tx.rs b/script/src/syscalls/load_tx.rs index 5e933848fd..06b22a2715 100644 --- a/script/src/syscalls/load_tx.rs +++ b/script/src/syscalls/load_tx.rs @@ -3,6 +3,7 @@ use crate::{ syscalls::{ utils::store_data, LOAD_TRANSACTION_SYSCALL_NUMBER, LOAD_TX_HASH_SYSCALL_NUMBER, SUCCESS, }, + types::VmData, }; use ckb_types::{core::cell::ResolvedTransaction, prelude::*}; use ckb_vm::{ @@ -17,8 +18,10 @@ pub struct LoadTx { } impl LoadTx { - pub fn new(rtx: Arc) -> LoadTx { - LoadTx { rtx } + pub fn new
(vm_data: &Arc>) -> LoadTx { + LoadTx { + rtx: Arc::clone(&vm_data.sg_data.tx_data.rtx), + } } } diff --git a/script/src/syscalls/load_witness.rs b/script/src/syscalls/load_witness.rs index b5039be5c4..7a6acdb296 100644 --- a/script/src/syscalls/load_witness.rs +++ b/script/src/syscalls/load_witness.rs @@ -4,12 +4,9 @@ use crate::{ utils::store_data, Source, SourceEntry, INDEX_OUT_OF_BOUND, LOAD_WITNESS_SYSCALL_NUMBER, SUCCESS, }, - types::Indices, -}; -use ckb_types::{ - core::cell::ResolvedTransaction, - packed::{Bytes, BytesVec}, + types::VmData, }; +use ckb_types::packed::{Bytes, BytesVec}; use ckb_vm::{ registers::{A0, A3, A4, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -17,38 +14,32 @@ use ckb_vm::{ use std::sync::Arc; #[derive(Debug)] -pub struct LoadWitness { - rtx: Arc, - group_inputs: Indices, - group_outputs: Indices, +pub struct LoadWitness
{ + vm_data: Arc>, } -impl LoadWitness { - pub fn new( - rtx: Arc, - group_inputs: Indices, - group_outputs: Indices, - ) -> LoadWitness { +impl
LoadWitness
{ + pub fn new(vm_data: &Arc>) -> Self { LoadWitness { - rtx, - group_inputs, - group_outputs, + vm_data: Arc::clone(vm_data), } } #[inline] fn witnesses(&self) -> BytesVec { - self.rtx.transaction.witnesses() + self.vm_data.rtx().transaction.witnesses() } fn fetch_witness(&self, source: Source, index: usize) -> Option { match source { Source::Group(SourceEntry::Input) => self - .group_inputs + .vm_data + .group_inputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), Source::Group(SourceEntry::Output) => self - .group_outputs + .vm_data + .group_outputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), Source::Transaction(SourceEntry::Input) => self.witnesses().get(index), @@ -58,7 +49,7 @@ impl LoadWitness { } } -impl Syscalls for LoadWitness { +impl Syscalls for LoadWitness
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 24a4ddcef3..a0ed55c168 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -28,6 +28,8 @@ mod pause; #[cfg(test)] mod tests; +pub mod generator; + pub use self::close::Close; pub use self::current_cycles::CurrentCycles; pub use self::debugger::Debugger; diff --git a/script/src/syscalls/pipe.rs b/script/src/syscalls/pipe.rs index 3bb61ba22d..a20b2c0693 100644 --- a/script/src/syscalls/pipe.rs +++ b/script/src/syscalls/pipe.rs @@ -1,5 +1,6 @@ use crate::syscalls::{PIPE, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Message, PipeArgs, VmId}; +use crate::types::{Message, PipeArgs, VmContext, VmData, VmId}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -13,8 +14,14 @@ pub struct Pipe { } impl Pipe { - pub fn new(id: VmId, message_box: Arc>>) -> Self { - Self { id, message_box } + pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/syscalls/process_id.rs b/script/src/syscalls/process_id.rs index 55114ae18a..0971ae1cbe 100644 --- a/script/src/syscalls/process_id.rs +++ b/script/src/syscalls/process_id.rs @@ -1,8 +1,10 @@ use crate::syscalls::PROCESS_ID; +use crate::types::VmData; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; +use std::sync::Arc; #[derive(Debug, Default)] pub struct ProcessID { @@ -10,8 +12,8 @@ pub struct ProcessID { } impl ProcessID { - pub fn new(id: u64) -> Self { - Self { id } + pub fn new
(vm_data: &Arc>) -> Self { + Self { id: vm_data.vm_id } } } diff --git a/script/src/syscalls/read.rs b/script/src/syscalls/read.rs index 63976ef096..0f48095735 100644 --- a/script/src/syscalls/read.rs +++ b/script/src/syscalls/read.rs @@ -1,5 +1,6 @@ use crate::syscalls::{INVALID_FD, READ, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Fd, FdArgs, Message, VmId}; +use crate::types::{Fd, FdArgs, Message, VmContext, VmData, VmId}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A2, A7}, Error as VMError, Memory, Register, SupportMachine, Syscalls, @@ -13,8 +14,14 @@ pub struct Read { } impl Read { - pub fn new(id: VmId, message_box: Arc>>) -> Self { - Self { id, message_box } + pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/syscalls/spawn.rs b/script/src/syscalls/spawn.rs index f4c7e82b87..a9fca4332a 100644 --- a/script/src/syscalls/spawn.rs +++ b/script/src/syscalls/spawn.rs @@ -2,7 +2,7 @@ use crate::syscalls::{ Source, INDEX_OUT_OF_BOUND, SLICE_OUT_OF_BOUND, SOURCE_ENTRY_MASK, SOURCE_GROUP_FLAG, SPAWN, SPAWN_EXTRA_CYCLES_BASE, SPAWN_YIELD_CYCLES_BASE, }; -use crate::types::{DataLocation, DataPieceId, Fd, Message, SpawnArgs, TxData, VmId}; +use crate::types::{DataLocation, DataPieceId, Fd, Message, SpawnArgs, VmContext, VmData, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ machine::SupportMachine, @@ -20,22 +20,18 @@ where { id: VmId, message_box: Arc>>, - snapshot2_context: Arc>>>, + snapshot2_context: Arc>>>>, } impl
Spawn
where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { - pub fn new( - id: VmId, - message_box: Arc>>, - snapshot2_context: Arc>>>, - ) -> Self { + pub fn new(vm_data: &Arc>, vm_context: &VmContext
) -> Self { Self { - id, - message_box, - snapshot2_context, + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + snapshot2_context: Arc::clone(&vm_context.snapshot2_context), } } } diff --git a/script/src/syscalls/tests/utils.rs b/script/src/syscalls/tests/utils.rs index de9ca6f542..39f2fab59b 100644 --- a/script/src/syscalls/tests/utils.rs +++ b/script/src/syscalls/tests/utils.rs @@ -1,11 +1,20 @@ +use crate::{ + types::{DataPieceId, ScriptGroup, ScriptGroupType, ScriptVersion, SgData, TxData, VmData}, + verify_env::TxVerifyEnv, +}; +use ckb_chain_spec::consensus::ConsensusBuilder; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_types::{ bytes::Bytes, - core::{cell::CellMeta, Capacity, HeaderView}, - packed::{self, Byte32, CellOutput, OutPoint}, + core::{ + cell::{CellMeta, ResolvedTransaction}, + Capacity, HeaderBuilder, HeaderView, + }, + packed::{self, Byte32, CellOutput, OutPoint, Script}, prelude::*, }; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; +use std::sync::Arc; #[derive(Default, Clone)] pub(crate) struct MockDataLoader { @@ -52,3 +61,49 @@ pub(crate) fn build_cell_meta(capacity_bytes: usize, data: Bytes) -> CellMeta { mem_cell_data_hash: Some(data_hash), } } + +pub(crate) fn build_tx_data(rtx: Arc) -> TxData { + build_tx_data_with_loader(rtx, new_mock_data_loader()) +} + +pub(crate) fn build_tx_data_with_loader( + rtx: Arc, + data_loader: MockDataLoader, +) -> TxData { + let consensus = ConsensusBuilder::default().build(); + let tx_env = TxVerifyEnv::new_commit(&HeaderBuilder::default().build()); + + TxData { + rtx, + data_loader, + consensus: Arc::new(consensus), + tx_env: Arc::new(tx_env), + binaries_by_data_hash: HashMap::default(), + binaries_by_type_hash: HashMap::default(), + lock_groups: BTreeMap::default(), + type_groups: BTreeMap::default(), + outputs: Vec::new(), + } +} + +pub(crate) fn build_vm_data( + tx_data: Arc>, + input_indices: Vec, + output_indices: Vec, +) -> Arc> { + let script_group = ScriptGroup { + script: Script::default(), + group_type: ScriptGroupType::Lock, + input_indices, + output_indices, + }; + Arc::new(VmData { + sg_data: Arc::new(SgData { + tx_data, + script_version: ScriptVersion::latest(), + script_group, + program_data_piece_id: DataPieceId::CellDep(0), + }), + vm_id: 0, + }) +} diff --git a/script/src/syscalls/tests/vm_latest/syscalls_1.rs b/script/src/syscalls/tests/vm_latest/syscalls_1.rs index f7ca26ecac..b1684a1caa 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_1.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_1.rs @@ -1,3 +1,4 @@ +use crate::types::{VmContext, VmData}; use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use ckb_hash::blake2b_256; use ckb_types::{ @@ -14,7 +15,6 @@ use ckb_types::{ use ckb_vm::{ memory::{FLAG_DIRTY, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE}, registers::{A0, A1, A2, A3, A4, A5, A7}, - snapshot2::Snapshot2Context, CoreMachine, Error as VMError, Memory, Syscalls, RISCV_PAGESIZE, }; use proptest::{collection::size_range, prelude::*}; @@ -23,8 +23,6 @@ use std::sync::{Arc, Mutex}; use super::SCRIPT_VERSION; use crate::syscalls::{tests::utils::*, *}; -use crate::types::TxData; -use crate::types::{ScriptGroup, ScriptGroupType}; fn _test_load_cell_not_exist(data: &[u8]) -> Result<(), TestCaseError> { let mut machine = SCRIPT_VERSION.init_core_machine_without_limit(); @@ -47,10 +45,6 @@ fn _test_load_cell_not_exist(data: &[u8]) -> Result<(), TestCaseError> { let output = build_cell_meta(100, output_cell_data); let input_cell_data: Bytes = data.iter().rev().cloned().collect(); let input_cell = build_cell_meta(100, input_cell_data); - let outputs = Arc::new(vec![output]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -59,7 +53,15 @@ fn _test_load_cell_not_exist(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(load_cell.ecall(&mut machine).is_ok()); prop_assert_eq!(machine.registers()[A0], u64::from(INDEX_OUT_OF_BOUND)); @@ -89,10 +91,6 @@ fn _test_load_cell_all(data: &[u8]) -> Result<(), TestCaseError> { let output = build_cell_meta(100, output_cell_data); let input_cell_data: Bytes = data.iter().rev().cloned().collect(); let input_cell = build_cell_meta(100, input_cell_data); - let outputs = Arc::new(vec![output.clone()]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -101,7 +99,15 @@ fn _test_load_cell_all(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output.clone()]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); let input_correct_data = input_cell.cell_output.as_slice(); let output_correct_data = output.cell_output.as_slice(); @@ -179,10 +185,6 @@ fn _test_load_cell_from_group(data: &[u8], source: SourceEntry) -> Result<(), Te let output = build_cell_meta(100, output_cell_data); let input_cell_data: Bytes = data.iter().rev().cloned().collect(); let input_cell = build_cell_meta(100, input_cell_data); - let outputs = Arc::new(vec![output.clone()]); - let group_inputs = Arc::new(vec![0]); - let group_outputs = Arc::new(vec![0]); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -191,7 +193,15 @@ fn _test_load_cell_from_group(data: &[u8], source: SourceEntry) -> Result<(), Te resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output.clone()]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + + let mut load_cell = LoadCell::new(&vm_data); let input_correct_data = input_cell.cell_output.as_slice(); let output_correct_data = output.cell_output.as_slice(); @@ -267,11 +277,6 @@ fn _test_load_cell_out_of_bound(index: u64, source: u64) -> Result<(), TestCaseE let input_cell = build_cell_meta(100, data); - let outputs = Arc::new(vec![output]); - let group_inputs = Arc::new(vec![0]); - let group_outputs = Arc::new(vec![0]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -279,7 +284,16 @@ fn _test_load_cell_out_of_bound(index: u64, source: u64) -> Result<(), TestCaseE resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + + let mut load_cell = LoadCell::new(&vm_data); + prop_assert!(load_cell.ecall(&mut machine).is_ok()); prop_assert_eq!(machine.registers()[A0], u64::from(INDEX_OUT_OF_BOUND)); Ok(()) @@ -325,11 +339,6 @@ fn _test_load_cell_length(data: &[u8]) -> Result<(), TestCaseError> { let input_cell_data: Bytes = data.iter().rev().cloned().collect(); let input_cell = build_cell_meta(100, input_cell_data); - let outputs = Arc::new(vec![output]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -337,7 +346,15 @@ fn _test_load_cell_length(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); let input_correct_data = input_cell.cell_output.as_slice(); @@ -378,11 +395,6 @@ fn _test_load_cell_partial(data: &[u8], offset: u64) -> Result<(), TestCaseError let input_cell_data: Bytes = data.iter().rev().cloned().collect(); let input_cell = build_cell_meta(100, input_cell_data); - let outputs = Arc::new(vec![output]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -390,7 +402,15 @@ fn _test_load_cell_partial(data: &[u8], offset: u64) -> Result<(), TestCaseError resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); let input_correct_data = input_cell.cell_output.as_slice(); @@ -444,11 +464,6 @@ fn _test_load_cell_capacity(capacity: Capacity) -> Result<(), TestCaseError> { mem_cell_data_hash: Some(data_hash), }; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -456,7 +471,10 @@ fn _test_load_cell_capacity(capacity: Capacity) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(machine.memory_mut().store64(&size_addr, &16).is_ok()); @@ -505,11 +523,6 @@ fn _test_load_cell_occupied_capacity(data: &[u8]) -> Result<(), TestCaseError> { mem_cell_data_hash: Some(data_hash), }; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -517,7 +530,10 @@ fn _test_load_cell_occupied_capacity(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(machine.memory_mut().store64(&size_addr, &16).is_ok()); @@ -566,10 +582,6 @@ fn test_load_missing_data_hash() { mem_cell_data: None, mem_cell_data_hash: None, }; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -578,7 +590,10 @@ fn test_load_missing_data_hash() { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); assert!(machine.memory_mut().store64(&size_addr, &100).is_ok()); @@ -612,10 +627,6 @@ fn _test_load_missing_contract(field: CellField) { machine.set_register(A7, LOAD_CELL_BY_FIELD_SYSCALL_NUMBER); // syscall number let output_cell = build_cell_meta(100, Bytes::new()); - let outputs = Arc::new(vec![output_cell]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -624,7 +635,15 @@ fn _test_load_missing_contract(field: CellField) { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = { + // Mutable TxData should only used in tests + let mut tx_data = build_tx_data(rtx); + tx_data.outputs = vec![output_cell]; + Arc::new(tx_data) + }; + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); assert!(machine.memory_mut().store64(&size_addr, &100).is_ok()); @@ -683,7 +702,6 @@ fn _test_load_header( headers, ..Default::default() }; - let group_inputs = Arc::new(vec![0]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() @@ -694,7 +712,10 @@ fn _test_load_header( resolved_dep_groups: vec![], }); - let mut load_header = LoadHeader::new(data_loader, rtx, group_inputs); + let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); + let vm_data = build_vm_data(tx_data, vec![0], vec![]); + + let mut load_header = LoadHeader::new(&vm_data); prop_assert!(machine .memory_mut() @@ -800,7 +821,6 @@ fn _test_load_header_by_field(data: &[u8], field: HeaderField) -> Result<(), Tes headers, ..Default::default() }; - let group_inputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() .header_dep(header.hash()) @@ -810,7 +830,10 @@ fn _test_load_header_by_field(data: &[u8], field: HeaderField) -> Result<(), Tes resolved_dep_groups: vec![], }); - let mut load_header = LoadHeader::new(data_loader, rtx, group_inputs); + let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_header = LoadHeader::new(&vm_data); prop_assert!(machine .memory_mut() @@ -860,7 +883,10 @@ fn _test_load_tx_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_tx = LoadTx::new(rtx); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_tx = LoadTx::new(&vm_data); prop_assert!(machine .memory_mut() @@ -911,7 +937,10 @@ fn _test_load_tx(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_tx = LoadTx::new(rtx); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_tx = LoadTx::new(&vm_data); prop_assert!(machine .memory_mut() @@ -955,7 +984,27 @@ fn _test_load_current_script_hash(data: &[u8]) -> Result<(), TestCaseError> { .build(); let hash = script.calc_script_hash(); let data = hash.raw_data(); - let mut load_script_hash = LoadScriptHash::new(hash); + + let rtx = Arc::new(ResolvedTransaction { + transaction: TransactionBuilder::default().build(), + resolved_cell_deps: vec![], + resolved_inputs: vec![], + resolved_dep_groups: vec![], + }); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + // Swap the internal script in VmData + let vm_data = { + let mut sg_data = vm_data.sg_data.as_ref().clone(); + sg_data.script_group.script = script; + Arc::new(VmData { + sg_data: Arc::new(sg_data), + vm_id: vm_data.vm_id, + }) + }; + + let mut load_script_hash = LoadScriptHash::new(&vm_data); prop_assert!(machine.memory_mut().store64(&size_addr, &64).is_ok()); @@ -1020,11 +1069,6 @@ fn _test_load_input_lock_script_hash(data: &[u8]) -> Result<(), TestCaseError> { .build(); input_cell.cell_output = output_with_lock; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -1032,7 +1076,10 @@ fn _test_load_input_lock_script_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(machine.memory_mut().store64(&size_addr, &64).is_ok()); @@ -1085,11 +1132,6 @@ fn _test_load_input_lock_script(data: &[u8]) -> Result<(), TestCaseError> { .build(); input_cell.cell_output = output_with_lock; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -1097,7 +1139,10 @@ fn _test_load_input_lock_script(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(machine .memory_mut() @@ -1153,11 +1198,6 @@ fn _test_load_input_type_script(data: &[u8]) -> Result<(), TestCaseError> { .build(); input_cell.cell_output = output_with_type; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![], @@ -1165,7 +1205,10 @@ fn _test_load_input_type_script(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(machine .memory_mut() @@ -1222,10 +1265,6 @@ fn _test_load_input_type_script_hash(data: &[u8]) -> Result<(), TestCaseError> { .type_(Some(script).pack()) .build(); input_cell.cell_output = output_with_type; - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -1234,7 +1273,10 @@ fn _test_load_input_type_script_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let mut load_cell = LoadCell::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_cell = LoadCell::new(&vm_data); prop_assert!(machine .memory_mut() @@ -1279,8 +1321,6 @@ fn _test_load_witness(data: &[u8], source: SourceEntry) -> Result<(), TestCaseEr let witness_correct_data = witness.raw_data(); let witnesses = vec![witness]; - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() .witnesses(witnesses.pack()) @@ -1290,7 +1330,10 @@ fn _test_load_witness(data: &[u8], source: SourceEntry) -> Result<(), TestCaseEr resolved_dep_groups: vec![], }); - let mut load_witness = LoadWitness::new(rtx, group_inputs, group_outputs); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let mut load_witness = LoadWitness::new(&vm_data); prop_assert!(machine .memory_mut() @@ -1344,8 +1387,6 @@ fn _test_load_group_witness(data: &[u8], source: SourceEntry) -> Result<(), Test let dummy_witness = Bytes::default().pack(); let witnesses = vec![dummy_witness, witness]; - let group_inputs = Arc::new(vec![1]); - let group_outputs = Arc::new(vec![1]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() .witnesses(witnesses.pack()) @@ -1354,7 +1395,11 @@ fn _test_load_group_witness(data: &[u8], source: SourceEntry) -> Result<(), Test resolved_inputs: vec![], resolved_dep_groups: vec![], }); - let mut load_witness = LoadWitness::new(rtx, group_inputs, group_outputs); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![1], vec![1]); + + let mut load_witness = LoadWitness::new(&vm_data); prop_assert!(machine .memory_mut() @@ -1401,7 +1446,26 @@ fn _test_load_script(data: &[u8]) -> Result<(), TestCaseError> { .build(); let script_correct_data = script.as_slice(); - let mut load_script = LoadScript::new(script.clone()); + let rtx = Arc::new(ResolvedTransaction { + transaction: TransactionBuilder::default().build(), + resolved_cell_deps: vec![], + resolved_inputs: vec![], + resolved_dep_groups: vec![], + }); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + // Swap the internal script in VmData + let vm_data = { + let mut sg_data = vm_data.sg_data.as_ref().clone(); + sg_data.script_group.script = script.clone(); + Arc::new(VmData { + sg_data: Arc::new(sg_data), + vm_id: vm_data.vm_id, + }) + }; + + let mut load_script = LoadScript::new(&vm_data); prop_assert!(machine .memory_mut() @@ -1455,7 +1519,6 @@ fn _test_load_cell_data_as_code( let dep_cell = build_cell_meta(10000, data.clone()); let input_cell = build_cell_meta(100, data.clone()); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() .output_data(data.pack()) @@ -1464,17 +1527,13 @@ fn _test_load_cell_data_as_code( resolved_inputs: vec![input_cell], resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: vec![0], - output_indices: vec![0], - }), - })))); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); prop_assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1527,7 +1586,6 @@ fn _test_load_cell_data( let data = Bytes::from(data.to_owned()); let dep_cell = build_cell_meta(10000, data.clone()); let input_cell = build_cell_meta(100, data.clone()); - let data_loader = new_mock_data_loader(); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() @@ -1537,17 +1595,13 @@ fn _test_load_cell_data( resolved_inputs: vec![input_cell], resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: vec![0], - output_indices: vec![0], - }), - })))); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); prop_assert!(load_code.ecall(&mut machine).is_ok()); @@ -1638,8 +1692,6 @@ fn test_load_overflowed_cell_data_as_code() { let dep_cell_data = Bytes::from(data); let dep_cell = build_cell_meta(10000, dep_cell_data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], @@ -1647,17 +1699,12 @@ fn test_load_overflowed_cell_data_as_code() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: Default::default(), - output_indices: Default::default(), - }), - })))); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1690,8 +1737,6 @@ fn _test_load_cell_data_on_freezed_memory(data: &[u8]) -> Result<(), TestCaseErr let dep_cell_data = Bytes::from(data.to_owned()); let dep_cell = build_cell_meta(10000, dep_cell_data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], @@ -1699,17 +1744,12 @@ fn _test_load_cell_data_on_freezed_memory(data: &[u8]) -> Result<(), TestCaseErr resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: Default::default(), - output_indices: Default::default(), - }), - })))); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); prop_assert!(load_code.ecall(&mut machine).is_err()); @@ -1740,8 +1780,6 @@ fn _test_load_cell_data_as_code_on_freezed_memory(data: &[u8]) -> Result<(), Tes let dep_cell_data = Bytes::from(data.to_owned()); let dep_cell = build_cell_meta(10000, dep_cell_data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], @@ -1749,17 +1787,12 @@ fn _test_load_cell_data_as_code_on_freezed_memory(data: &[u8]) -> Result<(), Tes resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: Default::default(), - output_indices: Default::default(), - }), - })))); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); prop_assert!(load_code.ecall(&mut machine).is_err()); @@ -1801,8 +1834,6 @@ fn test_load_code_unaligned_error() { let dep_cell_data = Bytes::from(data.to_vec()); let dep_cell = build_cell_meta(10000, dep_cell_data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], @@ -1810,17 +1841,12 @@ fn test_load_code_unaligned_error() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: Default::default(), - output_indices: Default::default(), - }), - })))); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1849,8 +1875,6 @@ fn test_load_code_slice_out_of_bound_error() { let dep_cell_data = Bytes::from(data.to_vec()); let dep_cell = build_cell_meta(10000, dep_cell_data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], @@ -1858,17 +1882,12 @@ fn test_load_code_slice_out_of_bound_error() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: Default::default(), - output_indices: Default::default(), - }), - })))); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1900,8 +1919,6 @@ fn test_load_code_not_enough_space_error() { let dep_cell_data = Bytes::from(data); let dep_cell = build_cell_meta(10000, dep_cell_data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], @@ -1909,17 +1926,12 @@ fn test_load_code_not_enough_space_error() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: Default::default(), - output_indices: Default::default(), - }), - })))); + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1980,8 +1992,11 @@ fn _test_load_input( resolved_inputs: vec![], resolved_dep_groups: vec![], }); - let group_inputs = Arc::new(vec![0]); - let mut load_input = LoadInput::new(rtx, group_inputs); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![0], vec![]); + + let mut load_input = LoadInput::new(&vm_data); let mut buffer = vec![]; let expect = if let Some(field) = field { @@ -2111,25 +2126,20 @@ fn test_load_cell_data_size_zero() { let dep_cell = build_cell_meta(10000, data.clone()); let input_cell = build_cell_meta(100, data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], resolved_inputs: vec![input_cell], resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: vec![0], - output_indices: vec![0], - }), - })))); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); + load_code.ecall(&mut machine).unwrap(); assert_eq!(machine.registers()[A0], u64::from(SUCCESS)); assert_eq!(machine.memory_mut().load64(&size_addr).unwrap(), 256); @@ -2156,25 +2166,20 @@ fn test_load_cell_data_size_zero_index_out_of_bound() { let dep_cell = build_cell_meta(10000, data.clone()); let input_cell = build_cell_meta(100, data); - let data_loader = new_mock_data_loader(); - let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), resolved_cell_deps: vec![dep_cell], resolved_inputs: vec![input_cell], resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { - rtx, - data_loader, - program: Bytes::new(), - script_group: Arc::new(ScriptGroup { - script: Default::default(), - group_type: ScriptGroupType::Lock, - input_indices: vec![0], - output_indices: vec![0], - }), - })))); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let mut load_code = LoadCellData::new(&vm_context); + load_code.ecall(&mut machine).unwrap(); assert_eq!(machine.registers()[A0], u64::from(INDEX_OUT_OF_BOUND)); } diff --git a/script/src/syscalls/tests/vm_latest/syscalls_2.rs b/script/src/syscalls/tests/vm_latest/syscalls_2.rs index 4cac872f56..f9991f3687 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_2.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_2.rs @@ -1,4 +1,4 @@ -use crate::syscalls::tests::utils::MockDataLoader; +use crate::{syscalls::tests::utils::*, types::VmContext}; use ckb_types::{ bytes::Bytes, core::{ @@ -53,7 +53,19 @@ fn test_current_cycles() { machine.set_cycles(cycles); - let result = CurrentCycles::new(Arc::new(Mutex::new(0))).ecall(&mut machine); + let rtx = Arc::new(ResolvedTransaction { + transaction: TransactionBuilder::default().build(), + resolved_cell_deps: vec![], + resolved_inputs: vec![], + resolved_dep_groups: vec![], + }); + + let tx_data = Arc::new(build_tx_data(rtx)); + let vm_data = build_vm_data(tx_data, vec![], vec![]); + + let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + + let result = CurrentCycles::new(&vm_context).ecall(&mut machine); assert!(result.unwrap()); assert_eq!(machine.registers()[A0], cycles); @@ -99,7 +111,6 @@ fn _test_load_extension( extensions, ..Default::default() }; - let group_inputs = Arc::new(vec![0]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default() @@ -110,8 +121,10 @@ fn _test_load_extension( resolved_dep_groups: vec![], }); - let mut load_block_extension: LoadBlockExtension = - LoadBlockExtension::new(data_loader, rtx, group_inputs); + let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); + let vm_data = build_vm_data(tx_data, vec![0], vec![]); + + let mut load_block_extension = LoadBlockExtension::new(&vm_data); prop_assert!(machine .memory_mut() diff --git a/script/src/syscalls/wait.rs b/script/src/syscalls/wait.rs index c9a49566aa..17592368f6 100644 --- a/script/src/syscalls/wait.rs +++ b/script/src/syscalls/wait.rs @@ -1,5 +1,6 @@ use crate::syscalls::{SPAWN_YIELD_CYCLES_BASE, WAIT}; -use crate::types::{Message, VmId, WaitArgs}; +use crate::types::{Message, VmContext, VmData, VmId, WaitArgs}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -13,8 +14,14 @@ pub struct Wait { } impl Wait { - pub fn new(id: VmId, message_box: Arc>>) -> Self { - Self { id, message_box } + pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/syscalls/write.rs b/script/src/syscalls/write.rs index a193f5589b..6d75d2d63b 100644 --- a/script/src/syscalls/write.rs +++ b/script/src/syscalls/write.rs @@ -1,5 +1,6 @@ use crate::syscalls::{INVALID_FD, SPAWN_YIELD_CYCLES_BASE, WRITE}; -use crate::types::{Fd, FdArgs, Message, VmId}; +use crate::types::{Fd, FdArgs, Message, VmContext, VmData, VmId}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A2, A7}, Error as VMError, Memory, Register, SupportMachine, Syscalls, @@ -13,8 +14,14 @@ pub struct Write { } impl Write { - pub fn new(id: VmId, message_box: Arc>>) -> Self { - Self { id, message_box } + pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + { + Self { + id: vm_data.vm_id, + message_box: Arc::clone(&vm_context.message_box), + } } } diff --git a/script/src/types.rs b/script/src/types.rs index 8f25a98799..44c43d44d3 100644 --- a/script/src/types.rs +++ b/script/src/types.rs @@ -1,14 +1,21 @@ +use crate::{error::ScriptError, verify_env::TxVerifyEnv}; +use ckb_chain_spec::consensus::Consensus; use ckb_types::{ - core::{Cycle, ScriptHashType}, - packed::{Byte32, Script}, + core::{ + cell::{CellMeta, ResolvedTransaction}, + Cycle, ScriptHashType, + }, + packed::{Byte32, CellOutput, OutPoint, Script}, + prelude::*, }; use ckb_vm::{ machine::{VERSION0, VERSION1, VERSION2}, ISA_B, ISA_IMC, ISA_MOP, }; use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap}; use std::fmt; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; #[cfg(has_asm)] use ckb_vm::machine::asm::{AsmCoreMachine, AsmMachine}; @@ -16,10 +23,9 @@ use ckb_vm::machine::asm::{AsmCoreMachine, AsmMachine}; #[cfg(not(has_asm))] use ckb_vm::{DefaultCoreMachine, TraceMachine, WXorXMemory}; -use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; +use ckb_traits::CellDataProvider; use ckb_vm::snapshot2::Snapshot2Context; -use ckb_types::core::cell::ResolvedTransaction; use ckb_vm::{ bytes::Bytes, machine::Pause, @@ -54,10 +60,14 @@ pub(crate) type Machine = AsmMachine; #[cfg(not(has_asm))] pub(crate) type Machine = TraceMachine; -pub(crate) type Indices = Arc>; - pub(crate) type DebugPrinter = Arc; +pub struct DebugContext { + pub debug_printer: DebugPrinter, + #[cfg(test)] + pub skip_pause: Arc, +} + /// The version of CKB Script Verifier. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ScriptVersion { @@ -126,7 +136,7 @@ impl ScriptVersion { /// A script group will only be executed once per transaction, the /// script itself should check against all inputs/outputs in its group /// if needed. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ScriptGroup { /// The script. /// @@ -140,9 +150,19 @@ pub struct ScriptGroup { pub output_indices: Vec, } +/// The methods included here are defected in a way: all construction +/// methods here create ScriptGroup without any `input_indices` or +/// `output_indices` filled. One has to manually fill them later(or forgot +/// about this). +/// As a result, we are marking them as crate-only methods for now. This +/// forces users to one of the following 2 solutions: +/// * Call `groups()` on `TxData` so they can fetch `ScriptGroup` data with +/// all correct data filled. +/// * Manually construct the struct where they have to think what shall be +/// used for `input_indices` and `output_indices`. impl ScriptGroup { /// Creates a new script group struct. - pub fn new(script: &Script, group_type: ScriptGroupType) -> Self { + pub(crate) fn new(script: &Script, group_type: ScriptGroupType) -> Self { Self { group_type, script: script.to_owned(), @@ -152,12 +172,12 @@ impl ScriptGroup { } /// Creates a lock script group. - pub fn from_lock_script(script: &Script) -> Self { + pub(crate) fn from_lock_script(script: &Script) -> Self { Self::new(script, ScriptGroupType::Lock) } /// Creates a type script group. - pub fn from_type_script(script: &Script) -> Self { + pub(crate) fn from_type_script(script: &Script) -> Self { Self::new(script, ScriptGroupType::Type) } } @@ -186,6 +206,7 @@ impl fmt::Display for ScriptGroupType { /// Struct specifies which script has verified so far. /// State is lifetime free, but capture snapshot need heavy memory copy +#[derive(Clone)] pub struct TransactionState { /// current suspended script index pub current: usize, @@ -257,34 +278,6 @@ pub enum ChunkCommand { Stop, } -#[derive(Clone)] -pub struct MachineContext< - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, -> { - pub(crate) base_cycles: Arc>, - pub(crate) snapshot2_context: Arc>>>, -} - -impl
MachineContext
-where - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, -{ - pub fn new(tx_data: TxData
) -> Self { - Self { - base_cycles: Arc::new(Mutex::new(0)), - snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(tx_data))), - } - } - - pub fn snapshot2_context(&self) -> &Arc>>> { - &self.snapshot2_context - } - - pub fn set_base_cycles(&mut self, base_cycles: u64) { - *self.base_cycles.lock().expect("lock") = base_cycles; - } -} - pub type VmId = u64; pub const FIRST_VM_ID: VmId = 0; @@ -408,8 +401,6 @@ pub enum Message { /// A pointer to the data that is part of the transaction. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum DataPieceId { - /// Target program. Usually located in cell data. - Program, /// The nth input cell data. Input(u32), /// The nth output data. @@ -495,62 +486,401 @@ impl FullSuspendedState { } } -/// Context data for current running transaction & script -#[derive(Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum DataGuard { + NotLoaded(OutPoint), + Loaded(Bytes), +} + +/// LazyData wrapper make sure not-loaded data will be loaded only after one access +#[derive(Debug, Clone)] +pub struct LazyData(Arc>); + +impl LazyData { + fn from_cell_meta(cell_meta: &CellMeta) -> LazyData { + match &cell_meta.mem_cell_data { + Some(data) => LazyData(Arc::new(RwLock::new(DataGuard::Loaded(data.to_owned())))), + None => LazyData(Arc::new(RwLock::new(DataGuard::NotLoaded( + cell_meta.out_point.clone(), + )))), + } + } + + fn access(&self, data_loader: &DL) -> Result { + let guard = self + .0 + .read() + .map_err(|_| ScriptError::Other("RwLock poisoned".into()))? + .to_owned(); + match guard { + DataGuard::NotLoaded(out_point) => { + let data = data_loader + .get_cell_data(&out_point) + .ok_or(ScriptError::Other("cell data not found".into()))?; + let mut write_guard = self + .0 + .write() + .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?; + *write_guard = DataGuard::Loaded(data.clone()); + Ok(data) + } + DataGuard::Loaded(bytes) => Ok(bytes), + } + } +} + +#[derive(Debug, Clone)] +pub enum Binaries { + Unique(Byte32, usize, LazyData), + Duplicate(Byte32, usize, LazyData), + Multiple, +} + +impl Binaries { + fn new(data_hash: Byte32, dep_index: usize, data: LazyData) -> Self { + Self::Unique(data_hash, dep_index, data) + } + + fn merge(&mut self, data_hash: &Byte32) { + match self { + Self::Unique(ref hash, dep_index, data) + | Self::Duplicate(ref hash, dep_index, data) => { + if hash != data_hash { + *self = Self::Multiple; + } else { + *self = Self::Duplicate(hash.to_owned(), *dep_index, data.to_owned()); + } + } + Self::Multiple => {} + } + } +} + +/// Immutable context data at transaction level +#[derive(Clone, Debug)] pub struct TxData
{ /// ResolvedTransaction. pub rtx: Arc, /// Data loader. pub data_loader: DL, - /// Ideally one might not want to keep program here, since program is totally - /// deducible from rtx + data_loader, however, for a demo here, program - /// does help us save some extra coding. - pub program: Bytes, - /// The script group to which the current program belongs. - pub script_group: Arc, + /// Chain consensus parameters + pub consensus: Arc, + /// Transaction verification environment + pub tx_env: Arc, + + /// Potential binaries in current transaction indexed by data hash + pub binaries_by_data_hash: HashMap, + /// Potential binaries in current transaction indexed by type script hash + pub binaries_by_type_hash: HashMap, + /// Lock script groups, orders here are important + pub lock_groups: BTreeMap, + /// Type script groups, orders here are important + pub type_groups: BTreeMap, + /// Output cells in current transaction reorganized in CellMeta format + pub outputs: Vec, +} + +impl
TxData
+where + DL: CellDataProvider, +{ + /// Creates a new TxData structure + pub fn new( + rtx: Arc, + data_loader: DL, + consensus: Arc, + tx_env: Arc, + ) -> Self { + let tx_hash = rtx.transaction.hash(); + let resolved_cell_deps = &rtx.resolved_cell_deps; + let resolved_inputs = &rtx.resolved_inputs; + let outputs = rtx + .transaction + .outputs_with_data_iter() + .enumerate() + .map(|(index, (cell_output, data))| { + let out_point = OutPoint::new_builder() + .tx_hash(tx_hash.clone()) + .index(index.pack()) + .build(); + let data_hash = CellOutput::calc_data_hash(&data); + CellMeta { + cell_output, + out_point, + transaction_info: None, + data_bytes: data.len() as u64, + mem_cell_data: Some(data), + mem_cell_data_hash: Some(data_hash), + } + }) + .collect(); + + let mut binaries_by_data_hash: HashMap = HashMap::default(); + let mut binaries_by_type_hash: HashMap = HashMap::default(); + for (i, cell_meta) in resolved_cell_deps.iter().enumerate() { + let data_hash = data_loader + .load_cell_data_hash(cell_meta) + .expect("cell data hash"); + let lazy = LazyData::from_cell_meta(cell_meta); + binaries_by_data_hash.insert(data_hash.to_owned(), (i, lazy.to_owned())); + + if let Some(t) = &cell_meta.cell_output.type_().to_opt() { + binaries_by_type_hash + .entry(t.calc_script_hash()) + .and_modify(|bin| bin.merge(&data_hash)) + .or_insert_with(|| Binaries::new(data_hash.to_owned(), i, lazy.to_owned())); + } + } + + let mut lock_groups = BTreeMap::default(); + let mut type_groups = BTreeMap::default(); + for (i, cell_meta) in resolved_inputs.iter().enumerate() { + // here we are only pre-processing the data, verify method validates + // each input has correct script setup. + let output = &cell_meta.cell_output; + let lock_group_entry = lock_groups + .entry(output.calc_lock_hash()) + .or_insert_with(|| ScriptGroup::from_lock_script(&output.lock())); + lock_group_entry.input_indices.push(i); + if let Some(t) = &output.type_().to_opt() { + let type_group_entry = type_groups + .entry(t.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_type_script(t)); + type_group_entry.input_indices.push(i); + } + } + for (i, output) in rtx.transaction.outputs().into_iter().enumerate() { + if let Some(t) = &output.type_().to_opt() { + let type_group_entry = type_groups + .entry(t.calc_script_hash()) + .or_insert_with(|| ScriptGroup::from_type_script(t)); + type_group_entry.output_indices.push(i); + } + } + + Self { + rtx, + data_loader, + consensus, + tx_env, + binaries_by_data_hash, + binaries_by_type_hash, + lock_groups, + type_groups, + outputs, + } + } + + #[inline] + /// Extracts actual script binary either in dep cells. + pub fn extract_script(&self, script: &Script) -> Result { + let (lazy, _) = self.extract_script_and_dep_index(script)?; + lazy.access(&self.data_loader) + } +} + +impl
TxData
{ + #[inline] + /// Extracts the index of the script binary in dep cells + pub fn extract_referenced_dep_index(&self, script: &Script) -> Result { + let (_, dep_index) = self.extract_script_and_dep_index(script)?; + Ok(*dep_index) + } + + fn extract_script_and_dep_index( + &self, + script: &Script, + ) -> Result<(&LazyData, &usize), ScriptError> { + let script_hash_type = ScriptHashType::try_from(script.hash_type()) + .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?; + match script_hash_type { + ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => { + if let Some((dep_index, lazy)) = self.binaries_by_data_hash.get(&script.code_hash()) + { + Ok((lazy, dep_index)) + } else { + Err(ScriptError::ScriptNotFound(script.code_hash())) + } + } + ScriptHashType::Type => { + if let Some(ref bin) = self.binaries_by_type_hash.get(&script.code_hash()) { + match bin { + Binaries::Unique(_, dep_index, ref lazy) => Ok((lazy, dep_index)), + Binaries::Duplicate(_, dep_index, ref lazy) => Ok((lazy, dep_index)), + Binaries::Multiple => Err(ScriptError::MultipleMatches), + } + } else { + Err(ScriptError::ScriptNotFound(script.code_hash())) + } + } + } + } + + #[inline] + /// Calculates transaction hash + pub fn tx_hash(&self) -> Byte32 { + self.rtx.transaction.hash() + } + + /// Finds the script group from cell deps. + pub fn find_script_group( + &self, + script_group_type: ScriptGroupType, + script_hash: &Byte32, + ) -> Option<&ScriptGroup> { + match script_group_type { + ScriptGroupType::Lock => self.lock_groups.get(script_hash), + ScriptGroupType::Type => self.type_groups.get(script_hash), + } + } + + fn is_vm_version_1_and_syscalls_2_enabled(&self) -> bool { + // If the proposal window is allowed to prejudge on the vm version, + // it will cause proposal tx to start a new vm in the blocks before hardfork, + // destroying the assumption that the transaction execution only uses the old vm + // before hardfork, leading to unexpected network splits. + let epoch_number = self.tx_env.epoch_number_without_proposal_window(); + let hardfork_switch = self.consensus.hardfork_switch(); + hardfork_switch + .ckb2021 + .is_vm_version_1_and_syscalls_2_enabled(epoch_number) + } + + fn is_vm_version_2_and_syscalls_3_enabled(&self) -> bool { + // If the proposal window is allowed to prejudge on the vm version, + // it will cause proposal tx to start a new vm in the blocks before hardfork, + // destroying the assumption that the transaction execution only uses the old vm + // before hardfork, leading to unexpected network splits. + let epoch_number = self.tx_env.epoch_number_without_proposal_window(); + let hardfork_switch = self.consensus.hardfork_switch(); + hardfork_switch + .ckb2023 + .is_vm_version_2_and_syscalls_3_enabled(epoch_number) + } + + /// Returns the version of the machine based on the script and the consensus rules. + pub fn select_version(&self, script: &Script) -> Result { + let is_vm_version_2_and_syscalls_3_enabled = self.is_vm_version_2_and_syscalls_3_enabled(); + let is_vm_version_1_and_syscalls_2_enabled = self.is_vm_version_1_and_syscalls_2_enabled(); + let script_hash_type = ScriptHashType::try_from(script.hash_type()) + .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?; + match script_hash_type { + ScriptHashType::Data => Ok(ScriptVersion::V0), + ScriptHashType::Data1 => { + if is_vm_version_1_and_syscalls_2_enabled { + Ok(ScriptVersion::V1) + } else { + Err(ScriptError::InvalidVmVersion(1)) + } + } + ScriptHashType::Data2 => { + if is_vm_version_2_and_syscalls_3_enabled { + Ok(ScriptVersion::V2) + } else { + Err(ScriptError::InvalidVmVersion(2)) + } + } + ScriptHashType::Type => { + if is_vm_version_2_and_syscalls_3_enabled { + Ok(ScriptVersion::V2) + } else if is_vm_version_1_and_syscalls_2_enabled { + Ok(ScriptVersion::V1) + } else { + Ok(ScriptVersion::V0) + } + } + } + } + + /// Returns all script groups. + pub fn groups(&self) -> impl Iterator { + self.lock_groups.iter().chain(self.type_groups.iter()) + } + + /// Returns all script groups with type. + pub fn groups_with_type( + &self, + ) -> impl Iterator { + self.lock_groups + .iter() + .map(|(hash, group)| (ScriptGroupType::Lock, hash, group)) + .chain( + self.type_groups + .iter() + .map(|(hash, group)| (ScriptGroupType::Type, hash, group)), + ) + } +} + +/// Immutable context data at script group level +#[derive(Clone, Debug)] +pub struct SgData
{ + /// Transaction level data + pub tx_data: Arc>, + + /// Currently executed script version + pub script_version: ScriptVersion, + /// Currently executed script group + pub script_group: ScriptGroup, + /// DataPieceId for the root program + pub program_data_piece_id: DataPieceId, } -impl
DataSource for TxData
+impl
SgData
{ + pub fn new(tx_data: &Arc>, script_group: &ScriptGroup) -> Result { + let script_version = tx_data.select_version(&script_group.script)?; + let dep_index = tx_data + .extract_referenced_dep_index(&script_group.script)? + .try_into() + .map_err(|_| ScriptError::Other("u32 overflow".to_string()))?; + Ok(Self { + tx_data: Arc::clone(tx_data), + script_version, + script_group: script_group.clone(), + program_data_piece_id: DataPieceId::CellDep(dep_index), + }) + } +} + +impl
DataSource for Arc> where - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, + DL: CellDataProvider, { fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> { match id { - DataPieceId::Program => { - // This is just a shortcut so we don't have to copy over the logic in extract_script, - // ideally you can also only define the rest 5, then figure out a way to convert - // script group to the actual cell dep index. - Some(self.program.clone()) - } DataPieceId::Input(i) => self + .tx_data .rtx .resolved_inputs .get(*i as usize) - .and_then(|cell| self.data_loader.load_cell_data(cell)), + .and_then(|cell| self.tx_data.data_loader.load_cell_data(cell)), DataPieceId::Output(i) => self + .tx_data .rtx .transaction .outputs_data() .get(*i as usize) .map(|data| data.raw_data()), DataPieceId::CellDep(i) => self + .tx_data .rtx .resolved_cell_deps .get(*i as usize) - .and_then(|cell| self.data_loader.load_cell_data(cell)), + .and_then(|cell| self.tx_data.data_loader.load_cell_data(cell)), DataPieceId::GroupInput(i) => self .script_group .input_indices .get(*i as usize) - .and_then(|gi| self.rtx.resolved_inputs.get(*gi)) - .and_then(|cell| self.data_loader.load_cell_data(cell)), + .and_then(|gi| self.tx_data.rtx.resolved_inputs.get(*gi)) + .and_then(|cell| self.tx_data.data_loader.load_cell_data(cell)), DataPieceId::GroupOutput(i) => self .script_group .output_indices .get(*i as usize) - .and_then(|gi| self.rtx.transaction.outputs_data().get(*gi)) + .and_then(|gi| self.tx_data.rtx.transaction.outputs_data().get(*gi)) .map(|data| data.raw_data()), DataPieceId::Witness(i) => self + .tx_data .rtx .transaction .witnesses() @@ -560,13 +890,13 @@ where .script_group .input_indices .get(*i as usize) - .and_then(|gi| self.rtx.transaction.witnesses().get(*gi)) + .and_then(|gi| self.tx_data.rtx.transaction.witnesses().get(*gi)) .map(|data| data.raw_data()), DataPieceId::WitnessGroupOutput(i) => self .script_group .output_indices .get(*i as usize) - .and_then(|gi| self.rtx.transaction.witnesses().get(*gi)) + .and_then(|gi| self.tx_data.rtx.transaction.witnesses().get(*gi)) .map(|data| data.raw_data()), } .map(|data| { @@ -582,6 +912,83 @@ where } } +/// Immutable context data at virtual machine level +#[derive(Clone, Debug)] +pub struct VmData
{ + /// Script group level data + pub sg_data: Arc>, + + /// Currently executed virtual machine ID + pub vm_id: VmId, +} + +impl
VmData
{ + pub fn rtx(&self) -> &ResolvedTransaction { + &self.sg_data.tx_data.rtx + } + + pub fn data_loader(&self) -> &DL { + &self.sg_data.tx_data.data_loader + } + + pub fn group_inputs(&self) -> &[usize] { + &self.sg_data.script_group.input_indices + } + + pub fn group_outputs(&self) -> &[usize] { + &self.sg_data.script_group.output_indices + } + + pub fn outputs(&self) -> &[CellMeta] { + &self.sg_data.tx_data.outputs + } + + pub fn current_script_hash(&self) -> Byte32 { + self.sg_data.script_group.script.calc_script_hash() + } +} + +impl
DataSource for Arc> +where + DL: CellDataProvider, +{ + fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> { + self.sg_data.load_data(id, offset, length) + } +} + +/// Mutable data at virtual machine level +#[derive(Clone)] +pub struct VmContext
+where + DL: CellDataProvider, +{ + pub(crate) base_cycles: Arc>, + /// A mutable reference to scheduler's message box + pub(crate) message_box: Arc>>, + pub(crate) snapshot2_context: Arc>>>>, +} + +impl
VmContext
+where + DL: CellDataProvider, +{ + /// Creates a new VM context. It is by design that parameters to this function + /// are references. It is a reminder that the inputs are designed to be shared + /// among different entities. + pub fn new(vm_data: &Arc>, message_box: &Arc>>) -> Self { + Self { + base_cycles: Arc::new(Mutex::new(0)), + message_box: Arc::clone(message_box), + snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(vm_data)))), + } + } + + pub fn set_base_cycles(&mut self, base_cycles: u64) { + *self.base_cycles.lock().expect("lock") = base_cycles; + } +} + /// The scheduler's running mode. #[derive(Clone)] pub enum RunMode { diff --git a/script/src/verify.rs b/script/src/verify.rs index a9315e3a79..aaa5fd41d0 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -1,21 +1,12 @@ use crate::scheduler::Scheduler; -#[cfg(test)] -use crate::syscalls::Pause; -use crate::syscalls::{InheritedFd, ProcessID}; -use crate::types::{DataPieceId, FullSuspendedState, Message, RunMode, TxData, VmId, FIRST_VM_ID}; #[cfg(not(target_family = "wasm"))] use crate::ChunkCommand; use crate::{ error::{ScriptError, TransactionScriptError}, - syscalls::{ - Close, CurrentCycles, Debugger, Exec, ExecV2, LoadBlockExtension, LoadCell, LoadCellData, - LoadHeader, LoadInput, LoadScript, LoadScriptHash, LoadTx, LoadWitness, Pipe, Read, Spawn, - VMVersion, Wait, Write, - }, type_id::TypeIdSystemScript, types::{ - CoreMachine, DebugPrinter, Indices, ScriptGroup, ScriptGroupType, ScriptVersion, - TransactionState, VerifyResult, + DebugContext, DebugPrinter, FullSuspendedState, RunMode, ScriptGroup, ScriptGroupType, + ScriptVersion, SgData, TransactionState, TxData, VerifyResult, }, verify_env::TxVerifyEnv, }; @@ -26,21 +17,14 @@ use ckb_logger::{debug, info}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_types::{ bytes::Bytes, - core::{ - cell::{CellMeta, ResolvedTransaction}, - Cycle, ScriptHashType, - }, - packed::{Byte32, CellOutput, OutPoint, Script}, + core::{cell::ResolvedTransaction, Cycle, ScriptHashType}, + packed::{Byte32, Script}, prelude::*, }; #[cfg(not(target_family = "wasm"))] use ckb_vm::machine::Pause as VMPause; -use ckb_vm::{snapshot2::Snapshot2Context, Error as VMInternalError, Syscalls}; -use std::sync::{Arc, Mutex}; -use std::{ - collections::{BTreeMap, HashMap}, - sync::RwLock, -}; +use ckb_vm::Error as VMInternalError; +use std::sync::Arc; #[cfg(not(target_family = "wasm"))] use tokio::sync::{ oneshot, @@ -69,317 +53,13 @@ impl ChunkState { } } -#[derive(Debug, PartialEq, Eq, Clone)] -enum DataGuard { - NotLoaded(OutPoint), - Loaded(Bytes), -} - -/// LazyData wrapper make sure not-loaded data will be loaded only after one access -#[derive(Debug, Clone)] -struct LazyData(Arc>); - -impl LazyData { - fn from_cell_meta(cell_meta: &CellMeta) -> LazyData { - match &cell_meta.mem_cell_data { - Some(data) => LazyData(Arc::new(RwLock::new(DataGuard::Loaded(data.to_owned())))), - None => LazyData(Arc::new(RwLock::new(DataGuard::NotLoaded( - cell_meta.out_point.clone(), - )))), - } - } - - fn access(&self, data_loader: &DL) -> Result { - let guard = self - .0 - .read() - .map_err(|_| ScriptError::Other("RwLock poisoned".into()))? - .to_owned(); - match guard { - DataGuard::NotLoaded(out_point) => { - let data = data_loader - .get_cell_data(&out_point) - .ok_or(ScriptError::Other("cell data not found".into()))?; - let mut write_guard = self - .0 - .write() - .map_err(|_| ScriptError::Other("RwLock poisoned".into()))?; - *write_guard = DataGuard::Loaded(data.clone()); - Ok(data) - } - DataGuard::Loaded(bytes) => Ok(bytes), - } - } -} - -#[derive(Debug, Clone)] -enum Binaries { - Unique(Byte32, LazyData), - Duplicate(Byte32, LazyData), - Multiple, -} - -impl Binaries { - fn new(data_hash: Byte32, data: LazyData) -> Self { - Self::Unique(data_hash, data) - } - - fn merge(&mut self, data_hash: &Byte32) { - match self { - Self::Unique(ref hash, data) | Self::Duplicate(ref hash, data) => { - if hash != data_hash { - *self = Self::Multiple; - } else { - *self = Self::Duplicate(hash.to_owned(), data.to_owned()); - } - } - Self::Multiple => {} - } - } -} - -/// Syscalls can be generated individually by TransactionScriptsSyscallsGenerator. -/// -/// TransactionScriptsSyscallsGenerator can be cloned. -#[derive(Clone)] -pub struct TransactionScriptsSyscallsGenerator
-where - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, -{ - pub(crate) base_cycles: Arc>, - pub(crate) data_loader: DL, - pub(crate) debug_printer: DebugPrinter, - pub(crate) message_box: Arc>>, - pub(crate) outputs: Arc>, - pub(crate) rtx: Arc, - #[cfg(test)] - pub(crate) skip_pause: Arc, - pub(crate) vm_id: VmId, -} - -impl
TransactionScriptsSyscallsGenerator
-where - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, -{ - /// Build syscall: current_cycles - pub fn build_current_cycles(&self) -> CurrentCycles { - CurrentCycles::new(Arc::clone(&self.base_cycles)) - } - - /// Build syscall: vm_version - pub fn build_vm_version(&self) -> VMVersion { - VMVersion::new() - } - - /// Build syscall: exec - pub fn build_exec(&self, group_inputs: Indices, group_outputs: Indices) -> Exec
{ - Exec::new( - self.data_loader.clone(), - Arc::clone(&self.rtx), - Arc::clone(&self.outputs), - group_inputs, - group_outputs, - ) - } - - /// Build syscall: exec. When script version >= V2, this exec implementation is used. - pub fn build_exec_v2(&self) -> ExecV2 { - ExecV2::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Build syscall: load_tx - pub fn build_load_tx(&self) -> LoadTx { - LoadTx::new(Arc::clone(&self.rtx)) - } - - /// Build syscall: load_cell - pub fn build_load_cell(&self, group_inputs: Indices, group_outputs: Indices) -> LoadCell
{ - LoadCell::new( - self.data_loader.clone(), - Arc::clone(&self.rtx), - Arc::clone(&self.outputs), - group_inputs, - group_outputs, - ) - } - - /// Build syscall: load_cell_data - pub fn build_load_cell_data( - &self, - snapshot2_context: Arc>>>, - ) -> LoadCellData
{ - LoadCellData::new(snapshot2_context) - } - - ///Build syscall: load_input - pub fn build_load_input(&self, group_inputs: Indices) -> LoadInput { - LoadInput::new(Arc::clone(&self.rtx), group_inputs) - } - - /// Build syscall: load_script_hash - pub fn build_load_script_hash(&self, hash: Byte32) -> LoadScriptHash { - LoadScriptHash::new(hash) - } - - /// Build syscall: load_header - pub fn build_load_header(&self, group_inputs: Indices) -> LoadHeader
{ - LoadHeader::new( - self.data_loader.clone(), - Arc::clone(&self.rtx), - group_inputs, - ) - } - - /// Build syscall: load_block_extension - pub fn build_load_block_extension(&self, group_inputs: Indices) -> LoadBlockExtension
{ - LoadBlockExtension::new( - self.data_loader.clone(), - Arc::clone(&self.rtx), - group_inputs, - ) - } - - /// Build syscall: load_witness - pub fn build_load_witness(&self, group_inputs: Indices, group_outputs: Indices) -> LoadWitness { - LoadWitness::new(Arc::clone(&self.rtx), group_inputs, group_outputs) - } - - /// Build syscall: load_script - pub fn build_load_script(&self, script: Script) -> LoadScript { - LoadScript::new(script) - } - - /// Build syscall: spawn - pub fn build_spawn( - &self, - snapshot2_context: Arc>>>, - ) -> Spawn
{ - Spawn::new(self.vm_id, Arc::clone(&self.message_box), snapshot2_context) - } - - /// Build syscall: wait - pub fn build_wait(&self) -> Wait { - Wait::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Build syscall: process_id - pub fn build_process_id(&self) -> ProcessID { - ProcessID::new(self.vm_id) - } - - /// Build syscall: pipe - pub fn build_pipe(&self) -> Pipe { - Pipe::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Build syscall: write - pub fn build_write(&self) -> Write { - Write::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Build syscall: read - pub fn build_read(&self) -> Read { - Read::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Build syscall: inherited_fd - pub fn inherited_fd(&self) -> InheritedFd { - InheritedFd::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Build syscall: close - pub fn close(&self) -> Close { - Close::new(self.vm_id, Arc::clone(&self.message_box)) - } - - /// Generate syscalls. - pub fn generate_syscalls( - &self, - script_version: ScriptVersion, - script_group: &ScriptGroup, - snapshot2_context: Arc>>>, - ) -> Vec)>> { - let current_script_hash = script_group.script.calc_script_hash(); - let script_group_input_indices = Arc::new(script_group.input_indices.clone()); - let script_group_output_indices = Arc::new(script_group.output_indices.clone()); - let mut syscalls: Vec)>> = vec![ - Box::new(self.build_load_script_hash(current_script_hash.clone())), - Box::new(self.build_load_tx()), - Box::new(self.build_load_cell( - Arc::clone(&script_group_input_indices), - Arc::clone(&script_group_output_indices), - )), - Box::new(self.build_load_input(Arc::clone(&script_group_input_indices))), - Box::new(self.build_load_header(Arc::clone(&script_group_input_indices))), - Box::new(self.build_load_witness( - Arc::clone(&script_group_input_indices), - Arc::clone(&script_group_output_indices), - )), - Box::new(self.build_load_script(script_group.script.clone())), - Box::new(self.build_load_cell_data(Arc::clone(&snapshot2_context))), - Box::new(Debugger::new( - current_script_hash, - Arc::clone(&self.debug_printer), - )), - ]; - if script_version >= ScriptVersion::V1 { - syscalls.append(&mut vec![ - Box::new(self.build_vm_version()), - if script_version >= ScriptVersion::V2 { - Box::new(self.build_exec_v2()) - } else { - Box::new(self.build_exec( - Arc::clone(&script_group_input_indices), - Arc::clone(&script_group_output_indices), - )) - }, - Box::new(self.build_current_cycles()), - ]); - } - if script_version >= ScriptVersion::V2 { - syscalls.append(&mut vec![ - Box::new(self.build_load_block_extension(Arc::clone(&script_group_input_indices))), - Box::new(self.build_spawn(Arc::clone(&snapshot2_context))), - Box::new(self.build_process_id()), - Box::new(self.build_pipe()), - Box::new(self.build_wait()), - Box::new(self.build_write()), - Box::new(self.build_read()), - Box::new(self.inherited_fd()), - Box::new(self.close()), - ]); - } - #[cfg(test)] - syscalls.push(Box::new(Pause::new(Arc::clone(&self.skip_pause)))); - syscalls - } -} - /// This struct leverages CKB VM to verify transaction inputs. -/// -/// FlatBufferBuilder owned `Vec` that grows as needed, in the -/// future, we might refactor this to share buffer to achieve zero-copy -pub struct TransactionScriptsVerifier
-where - DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, -{ - data_loader: DL, - - rtx: Arc, - - binaries_by_data_hash: HashMap, - binaries_by_type_hash: HashMap, - - lock_groups: BTreeMap, - type_groups: BTreeMap, +pub struct TransactionScriptsVerifier
{ + tx_data: Arc>, + debug_printer: DebugPrinter, #[cfg(test)] skip_pause: Arc, - - consensus: Arc, - tx_env: Arc, - - syscalls_generator: TransactionScriptsSyscallsGenerator
, } impl
TransactionScriptsVerifier
@@ -398,73 +78,7 @@ where consensus: Arc, tx_env: Arc, ) -> TransactionScriptsVerifier
{ - let tx_hash = rtx.transaction.hash(); - let resolved_cell_deps = &rtx.resolved_cell_deps; - let resolved_inputs = &rtx.resolved_inputs; - let outputs = Arc::new( - rtx.transaction - .outputs_with_data_iter() - .enumerate() - .map(|(index, (cell_output, data))| { - let out_point = OutPoint::new_builder() - .tx_hash(tx_hash.clone()) - .index(index.pack()) - .build(); - let data_hash = CellOutput::calc_data_hash(&data); - CellMeta { - cell_output, - out_point, - transaction_info: None, - data_bytes: data.len() as u64, - mem_cell_data: Some(data), - mem_cell_data_hash: Some(data_hash), - } - }) - .collect(), - ); - - let mut binaries_by_data_hash: HashMap = HashMap::default(); - let mut binaries_by_type_hash: HashMap = HashMap::default(); - for cell_meta in resolved_cell_deps { - let data_hash = data_loader - .load_cell_data_hash(cell_meta) - .expect("cell data hash"); - let lazy = LazyData::from_cell_meta(cell_meta); - binaries_by_data_hash.insert(data_hash.to_owned(), lazy.to_owned()); - - if let Some(t) = &cell_meta.cell_output.type_().to_opt() { - binaries_by_type_hash - .entry(t.calc_script_hash()) - .and_modify(|bin| bin.merge(&data_hash)) - .or_insert_with(|| Binaries::new(data_hash.to_owned(), lazy.to_owned())); - } - } - - let mut lock_groups = BTreeMap::default(); - let mut type_groups = BTreeMap::default(); - for (i, cell_meta) in resolved_inputs.iter().enumerate() { - // here we are only pre-processing the data, verify method validates - // each input has correct script setup. - let output = &cell_meta.cell_output; - let lock_group_entry = lock_groups - .entry(output.calc_lock_hash()) - .or_insert_with(|| ScriptGroup::from_lock_script(&output.lock())); - lock_group_entry.input_indices.push(i); - if let Some(t) = &output.type_().to_opt() { - let type_group_entry = type_groups - .entry(t.calc_script_hash()) - .or_insert_with(|| ScriptGroup::from_type_script(t)); - type_group_entry.input_indices.push(i); - } - } - for (i, output) in rtx.transaction.outputs().into_iter().enumerate() { - if let Some(t) = &output.type_().to_opt() { - let type_group_entry = type_groups - .entry(t.calc_script_hash()) - .or_insert_with(|| ScriptGroup::from_type_script(t)); - type_group_entry.output_indices.push(i); - } - } + let tx_data = Arc::new(TxData::new(rtx, data_loader, consensus, tx_env)); let debug_printer: DebugPrinter = Arc::new( #[allow(unused_variables)] @@ -473,33 +87,15 @@ where debug!("script group: {} DEBUG OUTPUT: {}", hash, message); }, ); + #[cfg(test)] let skip_pause = Arc::new(AtomicBool::new(false)); - let syscalls_generator = TransactionScriptsSyscallsGenerator { - base_cycles: Arc::new(Mutex::new(0)), - data_loader: data_loader.clone(), - debug_printer: Arc::clone(&debug_printer), - message_box: Arc::new(Mutex::new(Vec::new())), - outputs: Arc::clone(&outputs), - rtx: Arc::clone(&rtx), - #[cfg(test)] - skip_pause: Arc::clone(&skip_pause), - vm_id: FIRST_VM_ID, - }; - TransactionScriptsVerifier { - data_loader, - binaries_by_data_hash, - binaries_by_type_hash, - rtx, - lock_groups, - type_groups, + tx_data, + debug_printer, #[cfg(test)] skip_pause, - consensus, - tx_env, - syscalls_generator, } } @@ -513,7 +109,7 @@ where /// * `hash: &Byte32`: this is the script hash of currently running script group. /// * `message: &str`: message passed to the debug syscall. pub fn set_debug_printer(&mut self, func: F) { - self.syscalls_generator.debug_printer = Arc::new(func); + self.debug_printer = Arc::new(func); } #[cfg(test)] @@ -521,96 +117,54 @@ where self.skip_pause.store(skip_pause, Ordering::SeqCst); } + ////////////////////////////////////////////////////////////////// + // Functions below have been moved from verifier struct to TxData, + // however we still preserve all the public APIs by delegating + // them to TxData. + ////////////////////////////////////////////////////////////////// + #[inline] #[allow(dead_code)] fn hash(&self) -> Byte32 { - self.rtx.transaction.hash() + self.tx_data.tx_hash() } /// Extracts actual script binary either in dep cells. pub fn extract_script(&self, script: &Script) -> Result { - let script_hash_type = ScriptHashType::try_from(script.hash_type()) - .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?; - match script_hash_type { - ScriptHashType::Data | ScriptHashType::Data1 | ScriptHashType::Data2 => { - if let Some(lazy) = self.binaries_by_data_hash.get(&script.code_hash()) { - Ok(lazy.access(&self.data_loader)?) - } else { - Err(ScriptError::ScriptNotFound(script.code_hash())) - } - } - ScriptHashType::Type => { - if let Some(ref bin) = self.binaries_by_type_hash.get(&script.code_hash()) { - match bin { - Binaries::Unique(_, ref lazy) => Ok(lazy.access(&self.data_loader)?), - Binaries::Duplicate(_, ref lazy) => Ok(lazy.access(&self.data_loader)?), - Binaries::Multiple => Err(ScriptError::MultipleMatches), - } - } else { - Err(ScriptError::ScriptNotFound(script.code_hash())) - } - } - } + self.tx_data.extract_script(script) + } + + /// Returns the version of the machine based on the script and the consensus rules. + pub fn select_version(&self, script: &Script) -> Result { + self.tx_data.select_version(script) } - fn is_vm_version_1_and_syscalls_2_enabled(&self) -> bool { - // If the proposal window is allowed to prejudge on the vm version, - // it will cause proposal tx to start a new vm in the blocks before hardfork, - // destroying the assumption that the transaction execution only uses the old vm - // before hardfork, leading to unexpected network splits. - let epoch_number = self.tx_env.epoch_number_without_proposal_window(); - let hardfork_switch = self.consensus.hardfork_switch(); - hardfork_switch - .ckb2021 - .is_vm_version_1_and_syscalls_2_enabled(epoch_number) + /// Returns all script groups. + pub fn groups(&self) -> impl Iterator { + self.tx_data.groups() } - fn is_vm_version_2_and_syscalls_3_enabled(&self) -> bool { - // If the proposal window is allowed to prejudge on the vm version, - // it will cause proposal tx to start a new vm in the blocks before hardfork, - // destroying the assumption that the transaction execution only uses the old vm - // before hardfork, leading to unexpected network splits. - let epoch_number = self.tx_env.epoch_number_without_proposal_window(); - let hardfork_switch = self.consensus.hardfork_switch(); - hardfork_switch - .ckb2023 - .is_vm_version_2_and_syscalls_3_enabled(epoch_number) + /// Returns all script groups with type. + pub fn groups_with_type( + &self, + ) -> impl Iterator { + self.tx_data.groups_with_type() } - /// Returns the version of the machine based on the script and the consensus rules. - pub fn select_version(&self, script: &Script) -> Result { - let is_vm_version_2_and_syscalls_3_enabled = self.is_vm_version_2_and_syscalls_3_enabled(); - let is_vm_version_1_and_syscalls_2_enabled = self.is_vm_version_1_and_syscalls_2_enabled(); - let script_hash_type = ScriptHashType::try_from(script.hash_type()) - .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?; - match script_hash_type { - ScriptHashType::Data => Ok(ScriptVersion::V0), - ScriptHashType::Data1 => { - if is_vm_version_1_and_syscalls_2_enabled { - Ok(ScriptVersion::V1) - } else { - Err(ScriptError::InvalidVmVersion(1)) - } - } - ScriptHashType::Data2 => { - if is_vm_version_2_and_syscalls_3_enabled { - Ok(ScriptVersion::V2) - } else { - Err(ScriptError::InvalidVmVersion(2)) - } - } - ScriptHashType::Type => { - if is_vm_version_2_and_syscalls_3_enabled { - Ok(ScriptVersion::V2) - } else if is_vm_version_1_and_syscalls_2_enabled { - Ok(ScriptVersion::V1) - } else { - Ok(ScriptVersion::V0) - } - } - } + /// Finds the script group from cell deps. + pub fn find_script_group( + &self, + script_group_type: ScriptGroupType, + script_hash: &Byte32, + ) -> Option<&ScriptGroup> { + self.tx_data + .find_script_group(script_group_type, script_hash) } + ////////////////////////////////////////////////////////////////// + // This marks the end of delegated functions. + ////////////////////////////////////////////////////////////////// + /// Verifies the transaction by running scripts. /// /// ## Params @@ -724,77 +278,6 @@ where Ok(cycles) } - /// Resuming an suspended verify from snapshot - /// - /// ## Params - /// - /// * `snap` - Captured transaction verification state. - /// - /// * `limit_cycles` - Maximum allowed cycles to run the scripts. The verification quits early - /// when the consumed cycles exceed the limit. - /// - /// ## Returns - /// - /// It returns the total consumed cycles if verification completed, - /// If verify is suspended, a borrowed state will returned. - pub fn resume_from_snap( - &self, - snap: &TransactionState, - limit_cycles: Cycle, - ) -> Result { - let mut cycles = snap.current_cycles; - let mut current_used = 0; - - let (_hash, current_group) = self.groups().nth(snap.current).ok_or_else(|| { - ScriptError::Other(format!("snapshot group missing {:?}", snap.current)) - .unknown_source() - })?; - - // continue snapshot current script - match self.verify_group_with_chunk(current_group, limit_cycles, &snap.state) { - Ok(ChunkState::Completed(used_cycles, consumed_cycles)) => { - current_used = wrapping_cycles_add(current_used, consumed_cycles, current_group)?; - cycles = wrapping_cycles_add(cycles, used_cycles, current_group)?; - } - Ok(ChunkState::Suspended(state)) => { - let current = snap.current; - let state = TransactionState::new(state, current, cycles, limit_cycles); - return Ok(VerifyResult::Suspended(state)); - } - Err(e) => { - #[cfg(feature = "logging")] - logging::on_script_error(_hash, &self.hash(), &e); - return Err(e.source(current_group).into()); - } - } - - for (idx, (_hash, group)) in self.groups().enumerate().skip(snap.current + 1) { - let remain_cycles = limit_cycles.checked_sub(current_used).ok_or_else(|| { - ScriptError::Other(format!("expect invalid cycles {limit_cycles} {cycles}")) - .source(group) - })?; - - match self.verify_group_with_chunk(group, remain_cycles, &None) { - Ok(ChunkState::Completed(used_cycles, consumed_cycles)) => { - current_used = wrapping_cycles_add(current_used, consumed_cycles, group)?; - cycles = wrapping_cycles_add(cycles, used_cycles, group)?; - } - Ok(ChunkState::Suspended(state)) => { - let current = idx; - let state = TransactionState::new(state, current, cycles, remain_cycles); - return Ok(VerifyResult::Suspended(state)); - } - Err(e) => { - #[cfg(feature = "logging")] - logging::on_script_error(_hash, &self.hash(), &e); - return Err(e.source(group).into()); - } - } - } - - Ok(VerifyResult::Completed(cycles)) - } - /// Resuming an suspended verify from vm state /// /// ## Params @@ -810,7 +293,7 @@ where /// If verify is suspended, a borrowed state will returned. pub fn resume_from_state( &self, - state: TransactionState, + state: &TransactionState, limit_cycles: Cycle, ) -> Result { let TransactionState { @@ -821,14 +304,14 @@ where } = state; let mut current_used = 0; - let mut cycles = current_cycles; + let mut cycles = *current_cycles; - let (_hash, current_group) = self.groups().nth(current).ok_or_else(|| { + let (_hash, current_group) = self.groups().nth(*current).ok_or_else(|| { ScriptError::Other(format!("snapshot group missing {current:?}")).unknown_source() })?; let resumed_script_result = - self.verify_group_with_chunk(current_group, limit_cycles, &state); + self.verify_group_with_chunk(current_group, limit_cycles, state); match resumed_script_result { Ok(ChunkState::Completed(used_cycles, consumed_cycles)) => { @@ -836,7 +319,7 @@ where cycles = wrapping_cycles_add(cycles, used_cycles, current_group)?; } Ok(ChunkState::Suspended(state)) => { - let state = TransactionState::new(state, current, cycles, limit_cycles); + let state = TransactionState::new(state, *current, cycles, limit_cycles); return Ok(VerifyResult::Suspended(state)); } Err(e) => { @@ -968,7 +451,7 @@ where && Into::::into(group.script.hash_type()) == Into::::into(ScriptHashType::Type) { let verifier = TypeIdSystemScript { - rtx: &self.rtx, + rtx: &self.tx_data.rtx, script_group: group, max_cycles, }; @@ -977,24 +460,6 @@ where self.run(group, max_cycles) } } - /// Returns all script groups. - pub fn groups(&self) -> impl Iterator { - self.lock_groups.iter().chain(self.type_groups.iter()) - } - - /// Returns all script groups with type. - pub fn groups_with_type( - &self, - ) -> impl Iterator { - self.lock_groups - .iter() - .map(|(hash, group)| (ScriptGroupType::Lock, hash, group)) - .chain( - self.type_groups - .iter() - .map(|(hash, group)| (ScriptGroupType::Type, hash, group)), - ) - } fn verify_group_with_chunk( &self, @@ -1006,7 +471,7 @@ where && Into::::into(group.script.hash_type()) == Into::::into(ScriptHashType::Type) { let verifier = TypeIdSystemScript { - rtx: &self.rtx, + rtx: &self.tx_data.rtx, script_group: group, max_cycles, }; @@ -1026,24 +491,11 @@ where max_cycles: Cycle, state: &Option, ) -> Result { - let program = self.extract_script(&script_group.script)?; - let tx_data = TxData { - rtx: Arc::clone(&self.rtx), - data_loader: self.data_loader.clone(), - program, - script_group: Arc::new(script_group.clone()), - }; - let version = self.select_version(&script_group.script)?; let mut scheduler = if let Some(state) = state { - Scheduler::resume( - tx_data, - version, - self.syscalls_generator.clone(), - state.clone(), - ) + self.resume_scheduler(script_group, state) } else { - Scheduler::new(tx_data, version, self.syscalls_generator.clone()) - }; + self.create_scheduler(script_group) + }?; let previous_cycles = scheduler.consumed_cycles(); let res = scheduler.run(RunMode::LimitCycles(max_cycles)); match res { @@ -1083,7 +535,7 @@ where && Into::::into(group.script.hash_type()) == Into::::into(ScriptHashType::Type) { let verifier = TypeIdSystemScript { - rtx: &self.rtx, + rtx: &self.tx_data.rtx, script_group: group, max_cycles, }; @@ -1094,47 +546,33 @@ where } } - /// Finds the script group from cell deps. - pub fn find_script_group( - &self, - script_group_type: ScriptGroupType, - script_hash: &Byte32, - ) -> Option<&ScriptGroup> { - match script_group_type { - ScriptGroupType::Lock => self.lock_groups.get(script_hash), - ScriptGroupType::Type => self.type_groups.get(script_hash), - } - } - - /// Prepares syscalls. - pub fn generate_syscalls( + /// Create a scheduler to manage virtual machine instances. + pub fn create_scheduler( &self, - script_version: ScriptVersion, script_group: &ScriptGroup, - snapshot2_context: Arc>>>, - ) -> Vec)>> { - self.syscalls_generator - .generate_syscalls(script_version, script_group, snapshot2_context) + ) -> Result, ScriptError> { + let sg_data = SgData::new(&self.tx_data, script_group)?; + let debug_context = DebugContext { + debug_printer: Arc::clone(&self.debug_printer), + #[cfg(test)] + skip_pause: Arc::clone(&self.skip_pause), + }; + Ok(Scheduler::new(sg_data, debug_context)) } - /// Create a scheduler to manage virtual machine instances. - pub fn create_scheduler( + /// Resumes a scheduler from a previous state. + pub fn resume_scheduler( &self, script_group: &ScriptGroup, + state: &FullSuspendedState, ) -> Result, ScriptError> { - let program = self.extract_script(&script_group.script)?; - let tx_data = TxData { - rtx: Arc::clone(&self.rtx), - data_loader: self.data_loader.clone(), - program, - script_group: Arc::new(script_group.clone()), + let sg_data = SgData::new(&self.tx_data, script_group)?; + let debug_context = DebugContext { + debug_printer: Arc::clone(&self.debug_printer), + #[cfg(test)] + skip_pause: Arc::clone(&self.skip_pause), }; - let version = self.select_version(&script_group.script)?; - Ok(Scheduler::new( - tx_data, - version, - self.syscalls_generator.clone(), - )) + Ok(Scheduler::resume(sg_data, debug_context, state.clone())) } /// Runs a single program, then returns the exit code together with the entire @@ -1175,15 +613,7 @@ where max_cycles: Cycle, signal: &mut Receiver, ) -> Result { - let program = self.extract_script(&script_group.script)?; - let tx_data = TxData { - rtx: Arc::clone(&self.rtx), - data_loader: self.data_loader.clone(), - program, - script_group: Arc::new(script_group.clone()), - }; - let version = self.select_version(&script_group.script)?; - let mut scheduler = Scheduler::new(tx_data, version, self.syscalls_generator.clone()); + let mut scheduler = self.create_scheduler(script_group)?; let mut pause = VMPause::new(); let child_pause = pause.clone(); let (finish_tx, mut finish_rx) = oneshot::channel::>(); diff --git a/script/src/verify/tests/ckb_latest/features_since_v2019.rs b/script/src/verify/tests/ckb_latest/features_since_v2019.rs index a69c887575..74eccdef75 100644 --- a/script/src/verify/tests/ckb_latest/features_since_v2019.rs +++ b/script/src/verify/tests/ckb_latest/features_since_v2019.rs @@ -1193,7 +1193,7 @@ fn _check_typical_secp256k1_blake160_2_in_2_out_resume_load_cycles(step_cycles: loop { let state = init_state.take().unwrap(); let (limit_cycles, _last) = state.next_limit_cycles(step_cycles, TWO_IN_TWO_OUT_CYCLES); - match verifier.resume_from_state(state, limit_cycles).unwrap() { + match verifier.resume_from_state(&state, limit_cycles).unwrap() { VerifyResult::Suspended(state) => init_state = Some(state), VerifyResult::Completed(cycle) => { cycles = cycle; diff --git a/script/src/verify/tests/ckb_latest/features_since_v2021.rs b/script/src/verify/tests/ckb_latest/features_since_v2021.rs index 4d274616fe..32a3196dc2 100644 --- a/script/src/verify/tests/ckb_latest/features_since_v2021.rs +++ b/script/src/verify/tests/ckb_latest/features_since_v2021.rs @@ -729,7 +729,7 @@ fn _check_type_id_one_in_one_out_resume_with_state( loop { times += 1; let state = init_state.take().unwrap(); - match verifier.resume_from_state(state, limit).unwrap() { + match verifier.resume_from_state(&state, limit).unwrap() { VerifyResult::Suspended(state) => { init_state = Some(state); limit *= 2; @@ -860,7 +860,7 @@ fn _check_typical_secp256k1_blake160_2_in_2_out_tx_with_state(step_cycles: Cycle loop { let state = init_state.take().unwrap(); let (limit_cycles, _last) = state.next_limit_cycles(step_cycles, TWO_IN_TWO_OUT_CYCLES); - match verifier.resume_from_state(state, limit_cycles).unwrap() { + match verifier.resume_from_state(&state, limit_cycles).unwrap() { VerifyResult::Suspended(state) => init_state = Some(state), VerifyResult::Completed(cycle) => { cycles = cycle; @@ -927,7 +927,7 @@ fn _check_typical_secp256k1_blake160_2_in_2_out_tx_with_snap(step_cycles: Cycle) let snap = init_snap.take().unwrap(); let (limit_cycles, _last) = snap.next_limit_cycles(step_cycles, TWO_IN_TWO_OUT_CYCLES); - match verifier.resume_from_snap(&snap, limit_cycles).unwrap() { + match verifier.resume_from_state(&snap, limit_cycles).unwrap() { VerifyResult::Suspended(state) => { if count % 500 == 0 { init_snap = Some(state); @@ -944,7 +944,7 @@ fn _check_typical_secp256k1_blake160_2_in_2_out_tx_with_snap(step_cycles: Cycle) let state = init_state.take().unwrap(); let (limit_cycles, _last) = state.next_limit_cycles(step_cycles, TWO_IN_TWO_OUT_CYCLES); - match verifier.resume_from_state(state, limit_cycles).unwrap() { + match verifier.resume_from_state(&state, limit_cycles).unwrap() { VerifyResult::Suspended(state) => { if count % 500 == 0 { init_snap = Some(state); @@ -1020,7 +1020,7 @@ fn check_typical_secp256k1_blake160_2_in_2_out_tx_with_complete() { let snap = init_snap.take().unwrap(); let (limit_cycles, _last) = snap.next_limit_cycles(TWO_IN_TWO_OUT_CYCLES / 10, TWO_IN_TWO_OUT_CYCLES); - match verifier.resume_from_snap(&snap, limit_cycles).unwrap() { + match verifier.resume_from_state(&snap, limit_cycles).unwrap() { VerifyResult::Suspended(state) => init_snap = Some(state), VerifyResult::Completed(_) => { unreachable!() @@ -1164,7 +1164,7 @@ fn load_code_with_snapshot() { } let snap = init_snap.take().unwrap(); - let result = verifier.resume_from_snap(&snap, max_cycles); + let result = verifier.resume_from_state(&snap, max_cycles); match result.unwrap() { VerifyResult::Suspended(state) => { @@ -1264,7 +1264,7 @@ fn load_code_with_snapshot_more_times() { loop { let snap = init_snap.take().unwrap(); - let result = verifier.resume_from_snap(&snap, max_cycles); + let result = verifier.resume_from_state(&snap, max_cycles); match result.unwrap() { VerifyResult::Suspended(state) => { diff --git a/script/src/verify/tests/ckb_latest/features_since_v2023.rs b/script/src/verify/tests/ckb_latest/features_since_v2023.rs index 7ab47dfc9a..389fcd1600 100644 --- a/script/src/verify/tests/ckb_latest/features_since_v2023.rs +++ b/script/src/verify/tests/ckb_latest/features_since_v2023.rs @@ -8,6 +8,7 @@ use ckb_types::{ }; use proptest::prelude::*; use proptest::proptest; +use std::collections::{BTreeMap, HashMap}; fn simple_spawn_test(bin_path: &str, args: &[u8]) -> Result { let script_version = SCRIPT_VERSION; @@ -598,7 +599,7 @@ fn check_spawn_state() { loop { times += 1; let state: TransactionState = init_state.take().unwrap(); - match verifier.resume_from_snap(&state, max_cycles).unwrap() { + match verifier.resume_from_state(&state, max_cycles).unwrap() { VerifyResult::Suspended(state) => { init_state = Some(state); } diff --git a/script/src/verify/tests/utils.rs b/script/src/verify/tests/utils.rs index 035b8c3da9..1ebd0d0b8a 100644 --- a/script/src/verify/tests/utils.rs +++ b/script/src/verify/tests/utils.rs @@ -15,14 +15,14 @@ use ckb_test_chain_utils::{ use ckb_types::{ core::{ capacity_bytes, - cell::CellMetaBuilder, + cell::{CellMeta, CellMetaBuilder}, hardfork::{HardForks, CKB2021, CKB2023}, Capacity, Cycle, DepType, EpochNumber, EpochNumberWithFraction, HeaderView, ScriptHashType, TransactionBuilder, TransactionInfo, }, h256, packed::{ - Byte32, CellDep, CellInput, OutPoint, Script, TransactionInfoBuilder, + Byte32, CellDep, CellInput, CellOutput, OutPoint, Script, TransactionInfoBuilder, TransactionKeyBuilder, WitnessArgs, }, H256, @@ -246,7 +246,7 @@ impl TransactionScriptsVerifierWithEnv { loop { times += 1; let snap = init_snap.take().unwrap(); - match verifier.resume_from_snap(&snap, max_cycles) { + match verifier.resume_from_state(&snap, max_cycles) { Ok(VerifyResult::Suspended(state)) => { init_snap = Some(state); } From b459c3fcb323364c17b7e6375421a1d83defb957 Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Tue, 11 Feb 2025 05:48:10 +0000 Subject: [PATCH 4/8] Use AtomicU64 for base cycles instead of Mutex --- script/src/scheduler.rs | 28 ++++++++++++++++----------- script/src/syscalls/current_cycles.rs | 10 ++++++---- script/src/types.rs | 11 +++++++---- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/script/src/scheduler.rs b/script/src/scheduler.rs index 8e28ac2425..3e3f386728 100644 --- a/script/src/scheduler.rs +++ b/script/src/scheduler.rs @@ -23,7 +23,10 @@ use ckb_vm::{ Error, FlattenedArgsReader, Register, }; use std::collections::{BTreeMap, HashMap}; -use std::sync::{Arc, Mutex}; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, Mutex, +}; /// Root process's id. pub const ROOT_VM_ID: VmId = FIRST_VM_ID; @@ -78,7 +81,7 @@ where /// /// One can consider that +total_cycles+ contains the total cycles /// consumed in current scheduler, when the scheduler is not busy executing. - pub total_cycles: Arc>, + pub total_cycles: Arc, /// Iteration cycles, see +total_cycles+ on its usage pub iteration_cycles: Cycle, /// Next vm id used by spawn. @@ -112,7 +115,7 @@ where Self { sg_data: Arc::new(sg_data), debug_context, - total_cycles: Arc::new(Mutex::new(0)), + total_cycles: Arc::new(AtomicU64::new(0)), iteration_cycles: 0, next_vm_id: FIRST_VM_ID, next_fd_slot: FIRST_FD_SLOT, @@ -128,16 +131,19 @@ where /// Return total cycles. pub fn consumed_cycles(&self) -> Cycle { - *self.total_cycles.lock().expect("mutex") + self.total_cycles.load(Ordering::Acquire) } /// Add cycles to total cycles. pub fn consume_cycles(&mut self, cycles: Cycle) -> Result<(), Error> { - let mut total_cycles = self.total_cycles.lock().expect("mutex"); - *total_cycles = (*total_cycles) - .checked_add(cycles) - .ok_or(Error::CyclesExceeded)?; - Ok(()) + match self + .total_cycles + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |total_cycles| { + total_cycles.checked_add(cycles) + }) { + Ok(_) => Ok(()), + Err(_) => Err(Error::CyclesExceeded), + } } /// Resume a previously suspended scheduler state @@ -149,7 +155,7 @@ where let mut scheduler = Self { sg_data: Arc::new(sg_data), debug_context, - total_cycles: Arc::new(Mutex::new(full.total_cycles)), + total_cycles: Arc::new(AtomicU64::new(full.total_cycles)), iteration_cycles: 0, next_vm_id: full.next_vm_id, next_fd_slot: full.next_fd_slot, @@ -199,7 +205,7 @@ where // internal execution logic, it does not belong to VM execution // consensus. We are not charging cycles for suspending // a VM in the process of suspending the whole scheduler. - total_cycles: *self.total_cycles.lock().expect("mutex"), + total_cycles: self.total_cycles.load(Ordering::Acquire), next_vm_id: self.next_vm_id, next_fd_slot: self.next_fd_slot, vms, diff --git a/script/src/syscalls/current_cycles.rs b/script/src/syscalls/current_cycles.rs index d62684066a..14e13da807 100644 --- a/script/src/syscalls/current_cycles.rs +++ b/script/src/syscalls/current_cycles.rs @@ -4,11 +4,14 @@ use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::{Arc, Mutex}; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, +}; #[derive(Debug, Default)] pub struct CurrentCycles { - base: Arc>, + base: Arc, } impl CurrentCycles { @@ -33,8 +36,7 @@ impl Syscalls for CurrentCycles { } let cycles = self .base - .lock() - .map_err(|e| VMError::Unexpected(e.to_string()))? + .load(Ordering::Acquire) .checked_add(machine.cycles()) .ok_or(VMError::CyclesOverflow)?; machine.set_register(A0, Mac::REG::from_u64(cycles)); diff --git a/script/src/types.rs b/script/src/types.rs index 44c43d44d3..b0dc54be62 100644 --- a/script/src/types.rs +++ b/script/src/types.rs @@ -15,7 +15,10 @@ use ckb_vm::{ use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; use std::fmt; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, Mutex, RwLock, +}; #[cfg(has_asm)] use ckb_vm::machine::asm::{AsmCoreMachine, AsmMachine}; @@ -963,7 +966,7 @@ pub struct VmContext
where DL: CellDataProvider, { - pub(crate) base_cycles: Arc>, + pub(crate) base_cycles: Arc, /// A mutable reference to scheduler's message box pub(crate) message_box: Arc>>, pub(crate) snapshot2_context: Arc>>>>, @@ -978,14 +981,14 @@ where /// among different entities. pub fn new(vm_data: &Arc>, message_box: &Arc>>) -> Self { Self { - base_cycles: Arc::new(Mutex::new(0)), + base_cycles: Arc::new(AtomicU64::new(0)), message_box: Arc::clone(message_box), snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(vm_data)))), } } pub fn set_base_cycles(&mut self, base_cycles: u64) { - *self.base_cycles.lock().expect("lock") = base_cycles; + self.base_cycles.store(base_cycles, Ordering::Release); } } From ba3c101463a2dad3435b0e8c48b9036d21d9eba7 Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Wed, 12 Feb 2025 02:00:47 +0000 Subject: [PATCH 5/8] perf: Skip some clones and hash calculations --- script/src/syscalls/debugger.rs | 15 +++++++-------- script/src/syscalls/load_script.rs | 16 ++++++++-------- script/src/syscalls/load_script_hash.rs | 15 +++++++-------- .../src/syscalls/tests/vm_latest/syscalls_1.rs | 2 ++ script/src/types.rs | 8 ++++++-- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/script/src/syscalls/debugger.rs b/script/src/syscalls/debugger.rs index 88e21ad0a2..1e57502f34 100644 --- a/script/src/syscalls/debugger.rs +++ b/script/src/syscalls/debugger.rs @@ -1,27 +1,26 @@ use crate::types::{DebugContext, DebugPrinter, VmData}; use crate::{cost_model::transferred_byte_cycles, syscalls::DEBUG_PRINT_SYSCALL_NUMBER}; -use ckb_types::packed::Byte32; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Memory, Register, SupportMachine, Syscalls, }; use std::sync::Arc; -pub struct Debugger { - hash: Byte32, +pub struct Debugger
{ + vm_data: Arc>, printer: DebugPrinter, } -impl Debugger { - pub fn new
(vm_data: &Arc>, debug_context: &DebugContext) -> Debugger { +impl
Debugger
{ + pub fn new(vm_data: &Arc>, debug_context: &DebugContext) -> Debugger
{ Debugger { - hash: vm_data.current_script_hash(), + vm_data: Arc::clone(vm_data), printer: Arc::clone(&debug_context.debug_printer), } } } -impl Syscalls for Debugger { +impl Syscalls for Debugger
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } @@ -50,7 +49,7 @@ impl Syscalls for Debugger { machine.add_cycles_no_checking(transferred_byte_cycles(buffer.len() as u64))?; let s = String::from_utf8(buffer) .map_err(|e| VMError::External(format!("String from buffer {e:?}")))?; - (self.printer)(&self.hash, s.as_str()); + (self.printer)(self.vm_data.current_script_hash(), s.as_str()); Ok(true) } diff --git a/script/src/syscalls/load_script.rs b/script/src/syscalls/load_script.rs index 0f8290498d..7050e67647 100644 --- a/script/src/syscalls/load_script.rs +++ b/script/src/syscalls/load_script.rs @@ -3,7 +3,7 @@ use crate::{ syscalls::{utils::store_data, LOAD_SCRIPT_SYSCALL_NUMBER, SUCCESS}, types::VmData, }; -use ckb_types::{packed::Script, prelude::*}; +use ckb_types::prelude::*; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -11,19 +11,19 @@ use ckb_vm::{ use std::sync::Arc; #[derive(Debug)] -pub struct LoadScript { - script: Script, +pub struct LoadScript
{ + vm_data: Arc>, } -impl LoadScript { - pub fn new
(vm_data: &Arc>) -> Self { +impl
LoadScript
{ + pub fn new(vm_data: &Arc>) -> Self { Self { - script: vm_data.sg_data.script_group.script.clone(), + vm_data: Arc::clone(vm_data), } } } -impl Syscalls for LoadScript { +impl Syscalls for LoadScript
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } @@ -33,7 +33,7 @@ impl Syscalls for LoadScript { return Ok(false); } - let data = self.script.as_slice(); + let data = self.vm_data.sg_data.script_group.script.as_slice(); let wrote_size = store_data(machine, data)?; machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; diff --git a/script/src/syscalls/load_script_hash.rs b/script/src/syscalls/load_script_hash.rs index 5dd0dfa401..a8dbe14aca 100644 --- a/script/src/syscalls/load_script_hash.rs +++ b/script/src/syscalls/load_script_hash.rs @@ -3,7 +3,6 @@ use crate::{ syscalls::{utils::store_data, LOAD_SCRIPT_HASH_SYSCALL_NUMBER, SUCCESS}, types::VmData, }; -use ckb_types::packed::Byte32; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, @@ -11,19 +10,19 @@ use ckb_vm::{ use std::sync::Arc; #[derive(Debug)] -pub struct LoadScriptHash { - hash: Byte32, +pub struct LoadScriptHash
{ + vm_data: Arc>, } -impl LoadScriptHash { - pub fn new
(vm_data: &Arc>) -> LoadScriptHash { +impl
LoadScriptHash
{ + pub fn new(vm_data: &Arc>) -> LoadScriptHash
{ LoadScriptHash { - hash: vm_data.sg_data.script_group.script.calc_script_hash(), + vm_data: Arc::clone(vm_data), } } } -impl Syscalls for LoadScriptHash { +impl Syscalls for LoadScriptHash
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } @@ -33,7 +32,7 @@ impl Syscalls for LoadScriptHash { return Ok(false); } - let data = self.hash.as_reader().raw_data(); + let data = self.vm_data.current_script_hash().as_reader().raw_data(); let wrote_size = store_data(machine, data)?; machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; diff --git a/script/src/syscalls/tests/vm_latest/syscalls_1.rs b/script/src/syscalls/tests/vm_latest/syscalls_1.rs index b1684a1caa..3d0a8e8b83 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_1.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_1.rs @@ -997,6 +997,7 @@ fn _test_load_current_script_hash(data: &[u8]) -> Result<(), TestCaseError> { // Swap the internal script in VmData let vm_data = { let mut sg_data = vm_data.sg_data.as_ref().clone(); + sg_data.script_hash = script.calc_script_hash(); sg_data.script_group.script = script; Arc::new(VmData { sg_data: Arc::new(sg_data), @@ -1458,6 +1459,7 @@ fn _test_load_script(data: &[u8]) -> Result<(), TestCaseError> { // Swap the internal script in VmData let vm_data = { let mut sg_data = vm_data.sg_data.as_ref().clone(); + sg_data.script_hash = script.calc_script_hash(); sg_data.script_group.script = script.clone(); Arc::new(VmData { sg_data: Arc::new(sg_data), diff --git a/script/src/types.rs b/script/src/types.rs index b0dc54be62..a0ed256fb6 100644 --- a/script/src/types.rs +++ b/script/src/types.rs @@ -825,12 +825,15 @@ pub struct SgData
{ pub script_version: ScriptVersion, /// Currently executed script group pub script_group: ScriptGroup, + /// Currently executed script hash + pub script_hash: Byte32, /// DataPieceId for the root program pub program_data_piece_id: DataPieceId, } impl
SgData
{ pub fn new(tx_data: &Arc>, script_group: &ScriptGroup) -> Result { + let script_hash = script_group.script.calc_script_hash(); let script_version = tx_data.select_version(&script_group.script)?; let dep_index = tx_data .extract_referenced_dep_index(&script_group.script)? @@ -839,6 +842,7 @@ impl
SgData
{ Ok(Self { tx_data: Arc::clone(tx_data), script_version, + script_hash, script_group: script_group.clone(), program_data_piece_id: DataPieceId::CellDep(dep_index), }) @@ -946,8 +950,8 @@ impl
VmData
{ &self.sg_data.tx_data.outputs } - pub fn current_script_hash(&self) -> Byte32 { - self.sg_data.script_group.script.calc_script_hash() + pub fn current_script_hash(&self) -> &Byte32 { + &self.sg_data.script_hash } } From c731f33712018306a8aaaf027c9fea62d303db0b Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Wed, 12 Feb 2025 02:32:27 +0000 Subject: [PATCH 6/8] Fix test --- script/src/syscalls/tests/utils.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/src/syscalls/tests/utils.rs b/script/src/syscalls/tests/utils.rs index 39f2fab59b..506f35bddb 100644 --- a/script/src/syscalls/tests/utils.rs +++ b/script/src/syscalls/tests/utils.rs @@ -97,11 +97,13 @@ pub(crate) fn build_vm_data( input_indices, output_indices, }; + let script_hash = script_group.script.calc_script_hash(); Arc::new(VmData { sg_data: Arc::new(SgData { tx_data, script_version: ScriptVersion::latest(), script_group, + script_hash, program_data_piece_id: DataPieceId::CellDep(0), }), vm_id: 0, From 19c0b3c66744538633c8804cadf065210665313a Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Wed, 12 Feb 2025 12:38:02 +0800 Subject: [PATCH 7/8] Remove VmData structure per review comments --- script/src/scheduler.rs | 17 +- script/src/syscalls/close.rs | 6 +- script/src/syscalls/debugger.rs | 10 +- script/src/syscalls/exec.rs | 28 ++-- script/src/syscalls/exec_v2.rs | 9 +- script/src/syscalls/generator.rs | 45 +++--- script/src/syscalls/inherited_fd.rs | 6 +- script/src/syscalls/load_block_extension.rs | 20 +-- script/src/syscalls/load_cell.rs | 18 +-- script/src/syscalls/load_cell_data.rs | 4 +- script/src/syscalls/load_header.rs | 25 ++- script/src/syscalls/load_input.rs | 12 +- script/src/syscalls/load_script.rs | 10 +- script/src/syscalls/load_script_hash.rs | 10 +- script/src/syscalls/load_tx.rs | 6 +- script/src/syscalls/load_witness.rs | 14 +- script/src/syscalls/pipe.rs | 6 +- script/src/syscalls/process_id.rs | 7 +- script/src/syscalls/read.rs | 6 +- script/src/syscalls/spawn.rs | 8 +- script/src/syscalls/tests/utils.rs | 21 ++- .../syscalls/tests/vm_latest/syscalls_1.rs | 152 +++++++++--------- .../syscalls/tests/vm_latest/syscalls_2.rs | 8 +- script/src/syscalls/wait.rs | 6 +- script/src/syscalls/write.rs | 6 +- script/src/types.rs | 75 ++++----- 26 files changed, 248 insertions(+), 287 deletions(-) diff --git a/script/src/scheduler.rs b/script/src/scheduler.rs index 3e3f386728..4ace66ab37 100644 --- a/script/src/scheduler.rs +++ b/script/src/scheduler.rs @@ -6,7 +6,7 @@ use crate::syscalls::{ use crate::types::{ CoreMachineType, DataLocation, DataPieceId, DebugContext, Fd, FdArgs, FullSuspendedState, - Machine, Message, ReadState, RunMode, SgData, VmContext, VmData, VmId, VmState, WriteState, + Machine, Message, ReadState, RunMode, SgData, VmContext, VmId, VmState, WriteState, FIRST_FD_SLOT, FIRST_VM_ID, }; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; @@ -884,21 +884,20 @@ where // We will update max_cycles for each machine when it gets a chance to run u64::MAX, ); - let vm_data = Arc::new(VmData { - sg_data: Arc::clone(&self.sg_data), - vm_id: *id, - }); let vm_context = VmContext { base_cycles: Arc::clone(&self.total_cycles), message_box: Arc::clone(&self.message_box), - snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(&vm_data)))), + snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone( + &self.sg_data, + )))), }; let machine_builder = DefaultMachineBuilder::new(core_machine) .instruction_cycle_func(Box::new(estimate_cycles)); - let machine_builder = generate_ckb_syscalls(&vm_data, &vm_context, &self.debug_context) - .into_iter() - .fold(machine_builder, |builder, syscall| builder.syscall(syscall)); + let machine_builder = + generate_ckb_syscalls(id, &self.sg_data, &vm_context, &self.debug_context) + .into_iter() + .fold(machine_builder, |builder, syscall| builder.syscall(syscall)); let default_machine = machine_builder.build(); Ok((vm_context, Machine::new(default_machine))) } diff --git a/script/src/syscalls/close.rs b/script/src/syscalls/close.rs index 88c0124326..536465b9da 100644 --- a/script/src/syscalls/close.rs +++ b/script/src/syscalls/close.rs @@ -1,5 +1,5 @@ use crate::syscalls::{CLOSE, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Fd, Message, VmContext, VmData, VmId}; +use crate::types::{Fd, Message, VmContext, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A7}, @@ -14,12 +14,12 @@ pub struct Close { } impl Close { - pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + pub fn new
(vm_id: &VmId, vm_context: &VmContext
) -> Self where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/syscalls/debugger.rs b/script/src/syscalls/debugger.rs index 1e57502f34..d71732303e 100644 --- a/script/src/syscalls/debugger.rs +++ b/script/src/syscalls/debugger.rs @@ -1,4 +1,4 @@ -use crate::types::{DebugContext, DebugPrinter, VmData}; +use crate::types::{DebugContext, DebugPrinter, SgData}; use crate::{cost_model::transferred_byte_cycles, syscalls::DEBUG_PRINT_SYSCALL_NUMBER}; use ckb_vm::{ registers::{A0, A7}, @@ -7,14 +7,14 @@ use ckb_vm::{ use std::sync::Arc; pub struct Debugger
{ - vm_data: Arc>, + sg_data: Arc>, printer: DebugPrinter, } impl
Debugger
{ - pub fn new(vm_data: &Arc>, debug_context: &DebugContext) -> Debugger
{ + pub fn new(sg_data: &Arc>, debug_context: &DebugContext) -> Debugger
{ Debugger { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), printer: Arc::clone(&debug_context.debug_printer), } } @@ -49,7 +49,7 @@ impl Syscalls for Debugger
{ machine.add_cycles_no_checking(transferred_byte_cycles(buffer.len() as u64))?; let s = String::from_utf8(buffer) .map_err(|e| VMError::External(format!("String from buffer {e:?}")))?; - (self.printer)(self.vm_data.current_script_hash(), s.as_str()); + (self.printer)(self.sg_data.current_script_hash(), s.as_str()); Ok(true) } diff --git a/script/src/syscalls/exec.rs b/script/src/syscalls/exec.rs index 56b7fabc36..97140104fd 100644 --- a/script/src/syscalls/exec.rs +++ b/script/src/syscalls/exec.rs @@ -3,7 +3,7 @@ use crate::syscalls::{ Place, Source, SourceEntry, EXEC, INDEX_OUT_OF_BOUND, MAX_ARGV_LENGTH, SLICE_OUT_OF_BOUND, WRONG_FORMAT, }; -use crate::types::VmData; +use crate::types::SgData; use ckb_traits::CellDataProvider; use ckb_types::core::cell::CellMeta; use ckb_types::core::error::ARGV_TOO_LONG_TEXT; @@ -19,46 +19,46 @@ use std::sync::Arc; #[derive(Debug)] pub struct Exec
{ - vm_data: Arc>, + sg_data: Arc>, } impl Exec
{ - pub fn new(vm_data: &Arc>) -> Exec
{ + pub fn new(sg_data: &Arc>) -> Exec
{ Exec { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.vm_data.rtx().resolved_inputs + &self.sg_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.vm_data.rtx().resolved_cell_deps + &self.sg_data.rtx().resolved_cell_deps } #[inline] fn witnesses(&self) -> BytesVec { - self.vm_data.rtx().transaction.witnesses() + self.sg_data.rtx().transaction.witnesses() } fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { let cell_opt = match source { Source::Transaction(SourceEntry::Input) => self.resolved_inputs().get(index), - Source::Transaction(SourceEntry::Output) => self.vm_data.outputs().get(index), + Source::Transaction(SourceEntry::Output) => self.sg_data.outputs().get(index), Source::Transaction(SourceEntry::CellDep) => self.resolved_cell_deps().get(index), Source::Group(SourceEntry::Input) => self - .vm_data + .sg_data .group_inputs() .get(index) .and_then(|actual_index| self.resolved_inputs().get(*actual_index)), Source::Group(SourceEntry::Output) => self - .vm_data + .sg_data .group_outputs() .get(index) - .and_then(|actual_index| self.vm_data.outputs().get(*actual_index)), + .and_then(|actual_index| self.sg_data.outputs().get(*actual_index)), Source::Transaction(SourceEntry::HeaderDep) | Source::Group(SourceEntry::CellDep) | Source::Group(SourceEntry::HeaderDep) => { @@ -72,12 +72,12 @@ impl Exec
{ fn fetch_witness(&self, source: Source, index: usize) -> Result { let witness_opt = match source { Source::Group(SourceEntry::Input) => self - .vm_data + .sg_data .group_inputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), Source::Group(SourceEntry::Output) => self - .vm_data + .sg_data .group_outputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), @@ -116,7 +116,7 @@ impl Syscalls for return Ok(true); } let cell = cell.unwrap(); - self.vm_data + self.sg_data .data_loader() .load_cell_data(cell) .ok_or_else(|| { diff --git a/script/src/syscalls/exec_v2.rs b/script/src/syscalls/exec_v2.rs index 570c39a441..5f08ab39a9 100644 --- a/script/src/syscalls/exec_v2.rs +++ b/script/src/syscalls/exec_v2.rs @@ -1,5 +1,5 @@ use crate::syscalls::{EXEC, INDEX_OUT_OF_BOUND}; -use crate::types::{DataLocation, DataPieceId, ExecV2Args, Message, VmContext, VmData, VmId}; +use crate::types::{DataLocation, DataPieceId, ExecV2Args, Message, VmContext, VmId}; use ckb_traits::CellDataProvider; use ckb_vm::{ registers::{A0, A1, A2, A3, A4, A5, A7}, @@ -13,12 +13,9 @@ pub struct ExecV2 { } impl ExecV2 { - pub fn new( - vm_data: &Arc>, - vm_context: &VmContext
, - ) -> ExecV2 { + pub fn new(vm_id: &VmId, vm_context: &VmContext
) -> ExecV2 { ExecV2 { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/syscalls/generator.rs b/script/src/syscalls/generator.rs index 38a3c17cbf..c57adb8629 100644 --- a/script/src/syscalls/generator.rs +++ b/script/src/syscalls/generator.rs @@ -4,7 +4,7 @@ use crate::{ LoadCellData, LoadHeader, LoadInput, LoadScript, LoadScriptHash, LoadTx, LoadWitness, Pipe, ProcessID, Read, Spawn, VMVersion, Wait, Write, }, - types::{CoreMachine, DebugContext, ScriptVersion, VmContext, VmData}, + types::{CoreMachine, DebugContext, ScriptVersion, SgData, VmContext, VmId}, }; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::Syscalls; @@ -12,7 +12,8 @@ use std::sync::Arc; /// Generate RISC-V syscalls in CKB environment pub fn generate_ckb_syscalls
( - vm_data: &Arc>, + vm_id: &VmId, + sg_data: &Arc>, vm_context: &VmContext
, debug_context: &DebugContext, ) -> Vec)>> @@ -20,17 +21,17 @@ where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { let mut syscalls: Vec)>> = vec![ - Box::new(LoadScriptHash::new(vm_data)), - Box::new(LoadTx::new(vm_data)), - Box::new(LoadCell::new(vm_data)), - Box::new(LoadInput::new(vm_data)), - Box::new(LoadHeader::new(vm_data)), - Box::new(LoadWitness::new(vm_data)), - Box::new(LoadScript::new(vm_data)), + Box::new(LoadScriptHash::new(sg_data)), + Box::new(LoadTx::new(sg_data)), + Box::new(LoadCell::new(sg_data)), + Box::new(LoadInput::new(sg_data)), + Box::new(LoadHeader::new(sg_data)), + Box::new(LoadWitness::new(sg_data)), + Box::new(LoadScript::new(sg_data)), Box::new(LoadCellData::new(vm_context)), - Box::new(Debugger::new(vm_data, debug_context)), + Box::new(Debugger::new(sg_data, debug_context)), ]; - let script_version = &vm_data.sg_data.script_version; + let script_version = &sg_data.script_version; if script_version >= &ScriptVersion::V1 { syscalls.append(&mut vec![ Box::new(VMVersion::new()), @@ -38,20 +39,20 @@ where ]); } if script_version == &ScriptVersion::V1 { - syscalls.push(Box::new(Exec::new(vm_data))); + syscalls.push(Box::new(Exec::new(sg_data))); } if script_version >= &ScriptVersion::V2 { syscalls.append(&mut vec![ - Box::new(ExecV2::new(vm_data, vm_context)), - Box::new(LoadBlockExtension::new(vm_data)), - Box::new(Spawn::new(vm_data, vm_context)), - Box::new(ProcessID::new(vm_data)), - Box::new(Pipe::new(vm_data, vm_context)), - Box::new(Wait::new(vm_data, vm_context)), - Box::new(Write::new(vm_data, vm_context)), - Box::new(Read::new(vm_data, vm_context)), - Box::new(InheritedFd::new(vm_data, vm_context)), - Box::new(Close::new(vm_data, vm_context)), + Box::new(ExecV2::new(vm_id, vm_context)), + Box::new(LoadBlockExtension::new(sg_data)), + Box::new(Spawn::new(vm_id, vm_context)), + Box::new(ProcessID::new(vm_id)), + Box::new(Pipe::new(vm_id, vm_context)), + Box::new(Wait::new(vm_id, vm_context)), + Box::new(Write::new(vm_id, vm_context)), + Box::new(Read::new(vm_id, vm_context)), + Box::new(InheritedFd::new(vm_id, vm_context)), + Box::new(Close::new(vm_id, vm_context)), ]); } #[cfg(test)] diff --git a/script/src/syscalls/inherited_fd.rs b/script/src/syscalls/inherited_fd.rs index 489a1b5101..510b82969c 100644 --- a/script/src/syscalls/inherited_fd.rs +++ b/script/src/syscalls/inherited_fd.rs @@ -1,5 +1,5 @@ use crate::syscalls::{INHERITED_FD, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Fd, FdArgs, Message, VmContext, VmData, VmId}; +use crate::types::{Fd, FdArgs, Message, VmContext, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A7}, @@ -14,12 +14,12 @@ pub struct InheritedFd { } impl InheritedFd { - pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + pub fn new
(vm_id: &VmId, vm_context: &VmContext
) -> Self where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/syscalls/load_block_extension.rs b/script/src/syscalls/load_block_extension.rs index 81d7de6286..0594a890e3 100644 --- a/script/src/syscalls/load_block_extension.rs +++ b/script/src/syscalls/load_block_extension.rs @@ -4,7 +4,7 @@ use crate::{ utils::store_data, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_BLOCK_EXTENSION, SUCCESS, }, - types::VmData, + types::SgData, }; use ckb_traits::ExtensionProvider; use ckb_types::{ @@ -19,29 +19,29 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadBlockExtension
{ - vm_data: Arc>, + sg_data: Arc>, } impl LoadBlockExtension
{ - pub fn new(vm_data: &Arc>) -> LoadBlockExtension
{ + pub fn new(sg_data: &Arc>) -> LoadBlockExtension
{ LoadBlockExtension { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } #[inline] fn header_deps(&self) -> Byte32Vec { - self.vm_data.rtx().transaction.header_deps() + self.sg_data.rtx().transaction.header_deps() } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.vm_data.rtx().resolved_inputs + &self.sg_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.vm_data.rtx().resolved_cell_deps + &self.sg_data.rtx().resolved_cell_deps } fn load_block_extension(&self, cell_meta: &CellMeta) -> Option { @@ -55,7 +55,7 @@ impl LoadBlockExtension
{ .into_iter() .any(|hash| &hash == block_hash) { - self.vm_data.data_loader().get_block_extension(block_hash) + self.sg_data.data_loader().get_block_extension(block_hash) } else { None } @@ -79,13 +79,13 @@ impl LoadBlockExtension
{ .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|block_hash| { - self.vm_data + self.sg_data .data_loader() .get_block_extension(&block_hash) .ok_or(ITEM_MISSING) }), Source::Group(SourceEntry::Input) => self - .vm_data + .sg_data .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) diff --git a/script/src/syscalls/load_cell.rs b/script/src/syscalls/load_cell.rs index 4c71111bb1..78260c378b 100644 --- a/script/src/syscalls/load_cell.rs +++ b/script/src/syscalls/load_cell.rs @@ -4,7 +4,7 @@ use crate::{ utils::store_data, CellField, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_CELL_BY_FIELD_SYSCALL_NUMBER, LOAD_CELL_SYSCALL_NUMBER, SUCCESS, }, - types::{TxData, VmData}, + types::{SgData, TxData}, }; use byteorder::{LittleEndian, WriteBytesExt}; use ckb_traits::CellDataProvider; @@ -20,29 +20,29 @@ use ckb_vm::{ use std::sync::Arc; pub struct LoadCell
{ - vm_data: Arc>, + sg_data: Arc>, } impl LoadCell
{ - pub fn new(vm_data: &Arc>) -> LoadCell
{ + pub fn new(sg_data: &Arc>) -> LoadCell
{ LoadCell { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } #[inline] fn tx_data(&self) -> &TxData
{ - &self.vm_data.sg_data.tx_data + &self.sg_data.tx_data } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.vm_data.rtx().resolved_inputs + &self.sg_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.vm_data.rtx().resolved_cell_deps + &self.sg_data.rtx().resolved_cell_deps } fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { @@ -59,7 +59,7 @@ impl LoadCell
{ .ok_or(INDEX_OUT_OF_BOUND), Source::Transaction(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), Source::Group(SourceEntry::Input) => self - .vm_data + .sg_data .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) @@ -69,7 +69,7 @@ impl LoadCell
{ .ok_or(INDEX_OUT_OF_BOUND) }), Source::Group(SourceEntry::Output) => self - .vm_data + .sg_data .group_outputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) diff --git a/script/src/syscalls/load_cell_data.rs b/script/src/syscalls/load_cell_data.rs index c78f3dcec9..53b14b0d1c 100644 --- a/script/src/syscalls/load_cell_data.rs +++ b/script/src/syscalls/load_cell_data.rs @@ -1,4 +1,4 @@ -use crate::types::{DataPieceId, VmContext, VmData}; +use crate::types::{DataPieceId, SgData, VmContext}; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ @@ -20,7 +20,7 @@ pub struct LoadCellData
where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { - snapshot2_context: Arc>>>>, + snapshot2_context: Arc>>>>, } impl
LoadCellData
diff --git a/script/src/syscalls/load_header.rs b/script/src/syscalls/load_header.rs index 29df08cb04..5e503e3c1d 100644 --- a/script/src/syscalls/load_header.rs +++ b/script/src/syscalls/load_header.rs @@ -5,7 +5,7 @@ use crate::{ HeaderField, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_HEADER_BY_FIELD_SYSCALL_NUMBER, LOAD_HEADER_SYSCALL_NUMBER, SUCCESS, }, - types::VmData, + types::SgData, }; use ckb_traits::HeaderProvider; use ckb_types::{ @@ -21,13 +21,13 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadHeader
{ - vm_data: Arc>, + sg_data: Arc>, } impl LoadHeader
{ - pub fn new(vm_data: &Arc>) -> LoadHeader
{ + pub fn new(sg_data: &Arc>) -> LoadHeader
{ LoadHeader { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } @@ -37,22 +37,22 @@ impl LoadHeader
{ // resolved_cell_deps: &'a [CellMeta], #[inline] fn group_inputs(&self) -> &[usize] { - self.vm_data.group_inputs() + self.sg_data.group_inputs() } #[inline] fn header_deps(&self) -> Byte32Vec { - self.vm_data.rtx().transaction.header_deps() + self.sg_data.rtx().transaction.header_deps() } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.vm_data.rtx().resolved_inputs + &self.sg_data.rtx().resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.vm_data.rtx().resolved_cell_deps + &self.sg_data.rtx().resolved_cell_deps } fn load_header(&self, cell_meta: &CellMeta) -> Option { @@ -66,11 +66,7 @@ impl LoadHeader
{ .into_iter() .any(|hash| &hash == block_hash) { - self.vm_data - .sg_data - .tx_data - .data_loader - .get_header(block_hash) + self.sg_data.tx_data.data_loader.get_header(block_hash) } else { None } @@ -94,8 +90,7 @@ impl LoadHeader
{ .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|block_hash| { - self.vm_data - .sg_data + self.sg_data .tx_data .data_loader .get_header(&block_hash) diff --git a/script/src/syscalls/load_input.rs b/script/src/syscalls/load_input.rs index a7331e8756..5d0513a45e 100644 --- a/script/src/syscalls/load_input.rs +++ b/script/src/syscalls/load_input.rs @@ -4,7 +4,7 @@ use crate::{ utils::store_data, InputField, Source, SourceEntry, INDEX_OUT_OF_BOUND, LOAD_INPUT_BY_FIELD_SYSCALL_NUMBER, LOAD_INPUT_SYSCALL_NUMBER, SUCCESS, }, - types::VmData, + types::SgData, }; use byteorder::{LittleEndian, WriteBytesExt}; use ckb_types::{ @@ -19,19 +19,19 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadInput
{ - vm_data: Arc>, + sg_data: Arc>, } impl
LoadInput
{ - pub fn new(vm_data: &Arc>) -> Self { + pub fn new(sg_data: &Arc>) -> Self { LoadInput { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } #[inline] fn inputs(&self) -> CellInputVec { - self.vm_data.rtx().transaction.inputs() + self.sg_data.rtx().transaction.inputs() } fn fetch_input(&self, source: Source, index: usize) -> Result { @@ -43,7 +43,7 @@ impl
LoadInput
{ Source::Transaction(SourceEntry::CellDep) => Err(INDEX_OUT_OF_BOUND), Source::Transaction(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), Source::Group(SourceEntry::Input) => self - .vm_data + .sg_data .group_inputs() .get(index) .ok_or(INDEX_OUT_OF_BOUND) diff --git a/script/src/syscalls/load_script.rs b/script/src/syscalls/load_script.rs index 7050e67647..6ea2ec8d32 100644 --- a/script/src/syscalls/load_script.rs +++ b/script/src/syscalls/load_script.rs @@ -1,7 +1,7 @@ use crate::{ cost_model::transferred_byte_cycles, syscalls::{utils::store_data, LOAD_SCRIPT_SYSCALL_NUMBER, SUCCESS}, - types::VmData, + types::SgData, }; use ckb_types::prelude::*; use ckb_vm::{ @@ -12,13 +12,13 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadScript
{ - vm_data: Arc>, + sg_data: Arc>, } impl
LoadScript
{ - pub fn new(vm_data: &Arc>) -> Self { + pub fn new(sg_data: &Arc>) -> Self { Self { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } } @@ -33,7 +33,7 @@ impl Syscalls for LoadScript
{ return Ok(false); } - let data = self.vm_data.sg_data.script_group.script.as_slice(); + let data = self.sg_data.script_group.script.as_slice(); let wrote_size = store_data(machine, data)?; machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; diff --git a/script/src/syscalls/load_script_hash.rs b/script/src/syscalls/load_script_hash.rs index a8dbe14aca..73ecefa88e 100644 --- a/script/src/syscalls/load_script_hash.rs +++ b/script/src/syscalls/load_script_hash.rs @@ -1,7 +1,7 @@ use crate::{ cost_model::transferred_byte_cycles, syscalls::{utils::store_data, LOAD_SCRIPT_HASH_SYSCALL_NUMBER, SUCCESS}, - types::VmData, + types::SgData, }; use ckb_vm::{ registers::{A0, A7}, @@ -11,13 +11,13 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadScriptHash
{ - vm_data: Arc>, + sg_data: Arc>, } impl
LoadScriptHash
{ - pub fn new(vm_data: &Arc>) -> LoadScriptHash
{ + pub fn new(sg_data: &Arc>) -> LoadScriptHash
{ LoadScriptHash { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } } @@ -32,7 +32,7 @@ impl Syscalls for LoadScriptHash
return Ok(false); } - let data = self.vm_data.current_script_hash().as_reader().raw_data(); + let data = self.sg_data.current_script_hash().as_reader().raw_data(); let wrote_size = store_data(machine, data)?; machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; diff --git a/script/src/syscalls/load_tx.rs b/script/src/syscalls/load_tx.rs index 06b22a2715..fb4576682d 100644 --- a/script/src/syscalls/load_tx.rs +++ b/script/src/syscalls/load_tx.rs @@ -3,7 +3,7 @@ use crate::{ syscalls::{ utils::store_data, LOAD_TRANSACTION_SYSCALL_NUMBER, LOAD_TX_HASH_SYSCALL_NUMBER, SUCCESS, }, - types::VmData, + types::SgData, }; use ckb_types::{core::cell::ResolvedTransaction, prelude::*}; use ckb_vm::{ @@ -18,9 +18,9 @@ pub struct LoadTx { } impl LoadTx { - pub fn new
(vm_data: &Arc>) -> LoadTx { + pub fn new
(sg_data: &Arc>) -> LoadTx { LoadTx { - rtx: Arc::clone(&vm_data.sg_data.tx_data.rtx), + rtx: Arc::clone(&sg_data.tx_data.rtx), } } } diff --git a/script/src/syscalls/load_witness.rs b/script/src/syscalls/load_witness.rs index 7a6acdb296..09e1698db3 100644 --- a/script/src/syscalls/load_witness.rs +++ b/script/src/syscalls/load_witness.rs @@ -4,7 +4,7 @@ use crate::{ utils::store_data, Source, SourceEntry, INDEX_OUT_OF_BOUND, LOAD_WITNESS_SYSCALL_NUMBER, SUCCESS, }, - types::VmData, + types::SgData, }; use ckb_types::packed::{Bytes, BytesVec}; use ckb_vm::{ @@ -15,30 +15,30 @@ use std::sync::Arc; #[derive(Debug)] pub struct LoadWitness
{ - vm_data: Arc>, + sg_data: Arc>, } impl
LoadWitness
{ - pub fn new(vm_data: &Arc>) -> Self { + pub fn new(sg_data: &Arc>) -> Self { LoadWitness { - vm_data: Arc::clone(vm_data), + sg_data: Arc::clone(sg_data), } } #[inline] fn witnesses(&self) -> BytesVec { - self.vm_data.rtx().transaction.witnesses() + self.sg_data.rtx().transaction.witnesses() } fn fetch_witness(&self, source: Source, index: usize) -> Option { match source { Source::Group(SourceEntry::Input) => self - .vm_data + .sg_data .group_inputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), Source::Group(SourceEntry::Output) => self - .vm_data + .sg_data .group_outputs() .get(index) .and_then(|actual_index| self.witnesses().get(*actual_index)), diff --git a/script/src/syscalls/pipe.rs b/script/src/syscalls/pipe.rs index a20b2c0693..db7004b548 100644 --- a/script/src/syscalls/pipe.rs +++ b/script/src/syscalls/pipe.rs @@ -1,5 +1,5 @@ use crate::syscalls::{PIPE, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Message, PipeArgs, VmContext, VmData, VmId}; +use crate::types::{Message, PipeArgs, VmContext, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A7}, @@ -14,12 +14,12 @@ pub struct Pipe { } impl Pipe { - pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + pub fn new
(vm_id: &VmId, vm_context: &VmContext
) -> Self where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/syscalls/process_id.rs b/script/src/syscalls/process_id.rs index 0971ae1cbe..37b4df21e3 100644 --- a/script/src/syscalls/process_id.rs +++ b/script/src/syscalls/process_id.rs @@ -1,10 +1,9 @@ use crate::syscalls::PROCESS_ID; -use crate::types::VmData; +use crate::types::VmId; use ckb_vm::{ registers::{A0, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; #[derive(Debug, Default)] pub struct ProcessID { @@ -12,8 +11,8 @@ pub struct ProcessID { } impl ProcessID { - pub fn new
(vm_data: &Arc>) -> Self { - Self { id: vm_data.vm_id } + pub fn new(vm_id: &VmId) -> Self { + Self { id: *vm_id } } } diff --git a/script/src/syscalls/read.rs b/script/src/syscalls/read.rs index 0f48095735..7c96c7125b 100644 --- a/script/src/syscalls/read.rs +++ b/script/src/syscalls/read.rs @@ -1,5 +1,5 @@ use crate::syscalls::{INVALID_FD, READ, SPAWN_YIELD_CYCLES_BASE}; -use crate::types::{Fd, FdArgs, Message, VmContext, VmData, VmId}; +use crate::types::{Fd, FdArgs, Message, VmContext, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A2, A7}, @@ -14,12 +14,12 @@ pub struct Read { } impl Read { - pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + pub fn new
(vm_id: &VmId, vm_context: &VmContext
) -> Self where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/syscalls/spawn.rs b/script/src/syscalls/spawn.rs index a9fca4332a..6fb7d536eb 100644 --- a/script/src/syscalls/spawn.rs +++ b/script/src/syscalls/spawn.rs @@ -2,7 +2,7 @@ use crate::syscalls::{ Source, INDEX_OUT_OF_BOUND, SLICE_OUT_OF_BOUND, SOURCE_ENTRY_MASK, SOURCE_GROUP_FLAG, SPAWN, SPAWN_EXTRA_CYCLES_BASE, SPAWN_YIELD_CYCLES_BASE, }; -use crate::types::{DataLocation, DataPieceId, Fd, Message, SpawnArgs, VmContext, VmData, VmId}; +use crate::types::{DataLocation, DataPieceId, Fd, Message, SgData, SpawnArgs, VmContext, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ machine::SupportMachine, @@ -20,16 +20,16 @@ where { id: VmId, message_box: Arc>>, - snapshot2_context: Arc>>>>, + snapshot2_context: Arc>>>>, } impl
Spawn
where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { - pub fn new(vm_data: &Arc>, vm_context: &VmContext
) -> Self { + pub fn new(vm_id: &VmId, vm_context: &VmContext
) -> Self { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), snapshot2_context: Arc::clone(&vm_context.snapshot2_context), } diff --git a/script/src/syscalls/tests/utils.rs b/script/src/syscalls/tests/utils.rs index 506f35bddb..6d5d95330b 100644 --- a/script/src/syscalls/tests/utils.rs +++ b/script/src/syscalls/tests/utils.rs @@ -1,5 +1,5 @@ use crate::{ - types::{DataPieceId, ScriptGroup, ScriptGroupType, ScriptVersion, SgData, TxData, VmData}, + types::{DataPieceId, ScriptGroup, ScriptGroupType, ScriptVersion, SgData, TxData}, verify_env::TxVerifyEnv, }; use ckb_chain_spec::consensus::ConsensusBuilder; @@ -86,11 +86,11 @@ pub(crate) fn build_tx_data_with_loader( } } -pub(crate) fn build_vm_data( +pub(crate) fn build_sg_data( tx_data: Arc>, input_indices: Vec, output_indices: Vec, -) -> Arc> { +) -> Arc> { let script_group = ScriptGroup { script: Script::default(), group_type: ScriptGroupType::Lock, @@ -98,14 +98,11 @@ pub(crate) fn build_vm_data( output_indices, }; let script_hash = script_group.script.calc_script_hash(); - Arc::new(VmData { - sg_data: Arc::new(SgData { - tx_data, - script_version: ScriptVersion::latest(), - script_group, - script_hash, - program_data_piece_id: DataPieceId::CellDep(0), - }), - vm_id: 0, + Arc::new(SgData { + tx_data, + script_version: ScriptVersion::latest(), + script_group, + script_hash, + program_data_piece_id: DataPieceId::CellDep(0), }) } diff --git a/script/src/syscalls/tests/vm_latest/syscalls_1.rs b/script/src/syscalls/tests/vm_latest/syscalls_1.rs index 3d0a8e8b83..444a67a124 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_1.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_1.rs @@ -1,4 +1,4 @@ -use crate::types::{VmContext, VmData}; +use crate::types::VmContext; use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use ckb_hash::blake2b_256; use ckb_types::{ @@ -59,9 +59,9 @@ fn _test_load_cell_not_exist(data: &[u8]) -> Result<(), TestCaseError> { tx_data.outputs = vec![output]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(load_cell.ecall(&mut machine).is_ok()); prop_assert_eq!(machine.registers()[A0], u64::from(INDEX_OUT_OF_BOUND)); @@ -105,9 +105,9 @@ fn _test_load_cell_all(data: &[u8]) -> Result<(), TestCaseError> { tx_data.outputs = vec![output.clone()]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); let input_correct_data = input_cell.cell_output.as_slice(); let output_correct_data = output.cell_output.as_slice(); @@ -199,9 +199,9 @@ fn _test_load_cell_from_group(data: &[u8], source: SourceEntry) -> Result<(), Te tx_data.outputs = vec![output.clone()]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(tx_data, vec![0], vec![0]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); let input_correct_data = input_cell.cell_output.as_slice(); let output_correct_data = output.cell_output.as_slice(); @@ -290,9 +290,9 @@ fn _test_load_cell_out_of_bound(index: u64, source: u64) -> Result<(), TestCaseE tx_data.outputs = vec![output]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(tx_data, vec![0], vec![0]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(load_cell.ecall(&mut machine).is_ok()); prop_assert_eq!(machine.registers()[A0], u64::from(INDEX_OUT_OF_BOUND)); @@ -352,9 +352,9 @@ fn _test_load_cell_length(data: &[u8]) -> Result<(), TestCaseError> { tx_data.outputs = vec![output]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); let input_correct_data = input_cell.cell_output.as_slice(); @@ -408,9 +408,9 @@ fn _test_load_cell_partial(data: &[u8], offset: u64) -> Result<(), TestCaseError tx_data.outputs = vec![output]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); let input_correct_data = input_cell.cell_output.as_slice(); @@ -472,9 +472,9 @@ fn _test_load_cell_capacity(capacity: Capacity) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(machine.memory_mut().store64(&size_addr, &16).is_ok()); @@ -531,9 +531,9 @@ fn _test_load_cell_occupied_capacity(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(machine.memory_mut().store64(&size_addr, &16).is_ok()); @@ -591,9 +591,9 @@ fn test_load_missing_data_hash() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); assert!(machine.memory_mut().store64(&size_addr, &100).is_ok()); @@ -641,9 +641,9 @@ fn _test_load_missing_contract(field: CellField) { tx_data.outputs = vec![output_cell]; Arc::new(tx_data) }; - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); assert!(machine.memory_mut().store64(&size_addr, &100).is_ok()); @@ -713,9 +713,9 @@ fn _test_load_header( }); let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); - let vm_data = build_vm_data(tx_data, vec![0], vec![]); + let sg_data = build_sg_data(tx_data, vec![0], vec![]); - let mut load_header = LoadHeader::new(&vm_data); + let mut load_header = LoadHeader::new(&sg_data); prop_assert!(machine .memory_mut() @@ -831,9 +831,9 @@ fn _test_load_header_by_field(data: &[u8], field: HeaderField) -> Result<(), Tes }); let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_header = LoadHeader::new(&vm_data); + let mut load_header = LoadHeader::new(&sg_data); prop_assert!(machine .memory_mut() @@ -884,9 +884,9 @@ fn _test_load_tx_hash(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_tx = LoadTx::new(&vm_data); + let mut load_tx = LoadTx::new(&sg_data); prop_assert!(machine .memory_mut() @@ -938,9 +938,9 @@ fn _test_load_tx(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_tx = LoadTx::new(&vm_data); + let mut load_tx = LoadTx::new(&sg_data); prop_assert!(machine .memory_mut() @@ -993,19 +993,16 @@ fn _test_load_current_script_hash(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); // Swap the internal script in VmData - let vm_data = { - let mut sg_data = vm_data.sg_data.as_ref().clone(); + let sg_data = { + let mut sg_data = sg_data.as_ref().clone(); sg_data.script_hash = script.calc_script_hash(); sg_data.script_group.script = script; - Arc::new(VmData { - sg_data: Arc::new(sg_data), - vm_id: vm_data.vm_id, - }) + Arc::new(sg_data) }; - let mut load_script_hash = LoadScriptHash::new(&vm_data); + let mut load_script_hash = LoadScriptHash::new(&sg_data); prop_assert!(machine.memory_mut().store64(&size_addr, &64).is_ok()); @@ -1078,9 +1075,9 @@ fn _test_load_input_lock_script_hash(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(machine.memory_mut().store64(&size_addr, &64).is_ok()); @@ -1141,9 +1138,9 @@ fn _test_load_input_lock_script(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(machine .memory_mut() @@ -1207,9 +1204,9 @@ fn _test_load_input_type_script(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(machine .memory_mut() @@ -1275,9 +1272,9 @@ fn _test_load_input_type_script_hash(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_cell = LoadCell::new(&vm_data); + let mut load_cell = LoadCell::new(&sg_data); prop_assert!(machine .memory_mut() @@ -1332,9 +1329,9 @@ fn _test_load_witness(data: &[u8], source: SourceEntry) -> Result<(), TestCaseEr }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let mut load_witness = LoadWitness::new(&vm_data); + let mut load_witness = LoadWitness::new(&sg_data); prop_assert!(machine .memory_mut() @@ -1398,9 +1395,9 @@ fn _test_load_group_witness(data: &[u8], source: SourceEntry) -> Result<(), Test }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![1], vec![1]); + let sg_data = build_sg_data(tx_data, vec![1], vec![1]); - let mut load_witness = LoadWitness::new(&vm_data); + let mut load_witness = LoadWitness::new(&sg_data); prop_assert!(machine .memory_mut() @@ -1455,19 +1452,16 @@ fn _test_load_script(data: &[u8]) -> Result<(), TestCaseError> { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); // Swap the internal script in VmData - let vm_data = { - let mut sg_data = vm_data.sg_data.as_ref().clone(); + let sg_data = { + let mut sg_data = sg_data.as_ref().clone(); sg_data.script_hash = script.calc_script_hash(); sg_data.script_group.script = script.clone(); - Arc::new(VmData { - sg_data: Arc::new(sg_data), - vm_id: vm_data.vm_id, - }) + Arc::new(sg_data) }; - let mut load_script = LoadScript::new(&vm_data); + let mut load_script = LoadScript::new(&sg_data); prop_assert!(machine .memory_mut() @@ -1531,9 +1525,9 @@ fn _test_load_cell_data_as_code( }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(tx_data, vec![0], vec![0]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1599,9 +1593,9 @@ fn _test_load_cell_data( }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(tx_data, vec![0], vec![0]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1702,9 +1696,9 @@ fn test_load_overflowed_cell_data_as_code() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1747,9 +1741,9 @@ fn _test_load_cell_data_on_freezed_memory(data: &[u8]) -> Result<(), TestCaseErr }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1790,9 +1784,9 @@ fn _test_load_cell_data_as_code_on_freezed_memory(data: &[u8]) -> Result<(), Tes }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1844,9 +1838,9 @@ fn test_load_code_unaligned_error() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1885,9 +1879,9 @@ fn test_load_code_slice_out_of_bound_error() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1929,9 +1923,9 @@ fn test_load_code_not_enough_space_error() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -1996,9 +1990,9 @@ fn _test_load_input( }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![0], vec![]); + let sg_data = build_sg_data(tx_data, vec![0], vec![]); - let mut load_input = LoadInput::new(&vm_data); + let mut load_input = LoadInput::new(&sg_data); let mut buffer = vec![]; let expect = if let Some(field) = field { @@ -2136,9 +2130,9 @@ fn test_load_cell_data_size_zero() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(tx_data, vec![0], vec![0]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); @@ -2176,9 +2170,9 @@ fn test_load_cell_data_size_zero_index_out_of_bound() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(tx_data, vec![0], vec![0]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let mut load_code = LoadCellData::new(&vm_context); diff --git a/script/src/syscalls/tests/vm_latest/syscalls_2.rs b/script/src/syscalls/tests/vm_latest/syscalls_2.rs index f9991f3687..38b3f7c9ef 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_2.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_2.rs @@ -61,9 +61,9 @@ fn test_current_cycles() { }); let tx_data = Arc::new(build_tx_data(rtx)); - let vm_data = build_vm_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(tx_data, vec![], vec![]); - let vm_context = VmContext::new(&vm_data, &Arc::new(Mutex::new(Vec::new()))); + let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); let result = CurrentCycles::new(&vm_context).ecall(&mut machine); @@ -122,9 +122,9 @@ fn _test_load_extension( }); let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); - let vm_data = build_vm_data(tx_data, vec![0], vec![]); + let sg_data = build_sg_data(tx_data, vec![0], vec![]); - let mut load_block_extension = LoadBlockExtension::new(&vm_data); + let mut load_block_extension = LoadBlockExtension::new(&sg_data); prop_assert!(machine .memory_mut() diff --git a/script/src/syscalls/wait.rs b/script/src/syscalls/wait.rs index 17592368f6..11ae3693e3 100644 --- a/script/src/syscalls/wait.rs +++ b/script/src/syscalls/wait.rs @@ -1,5 +1,5 @@ use crate::syscalls::{SPAWN_YIELD_CYCLES_BASE, WAIT}; -use crate::types::{Message, VmContext, VmData, VmId, WaitArgs}; +use crate::types::{Message, VmContext, VmId, WaitArgs}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A7}, @@ -14,12 +14,12 @@ pub struct Wait { } impl Wait { - pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + pub fn new
(vm_id: &VmId, vm_context: &VmContext
) -> Self where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/syscalls/write.rs b/script/src/syscalls/write.rs index 6d75d2d63b..2784abbeb4 100644 --- a/script/src/syscalls/write.rs +++ b/script/src/syscalls/write.rs @@ -1,5 +1,5 @@ use crate::syscalls::{INVALID_FD, SPAWN_YIELD_CYCLES_BASE, WRITE}; -use crate::types::{Fd, FdArgs, Message, VmContext, VmData, VmId}; +use crate::types::{Fd, FdArgs, Message, VmContext, VmId}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ registers::{A0, A1, A2, A7}, @@ -14,12 +14,12 @@ pub struct Write { } impl Write { - pub fn new
(vm_data: &Arc>, vm_context: &VmContext
) -> Self + pub fn new
(vm_id: &VmId, vm_context: &VmContext
) -> Self where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { Self { - id: vm_data.vm_id, + id: *vm_id, message_box: Arc::clone(&vm_context.message_box), } } diff --git a/script/src/types.rs b/script/src/types.rs index a0ed256fb6..eee70ab348 100644 --- a/script/src/types.rs +++ b/script/src/types.rs @@ -847,6 +847,30 @@ impl
SgData
{ program_data_piece_id: DataPieceId::CellDep(dep_index), }) } + + pub fn rtx(&self) -> &ResolvedTransaction { + &self.tx_data.rtx + } + + pub fn data_loader(&self) -> &DL { + &self.tx_data.data_loader + } + + pub fn group_inputs(&self) -> &[usize] { + &self.script_group.input_indices + } + + pub fn group_outputs(&self) -> &[usize] { + &self.script_group.output_indices + } + + pub fn outputs(&self) -> &[CellMeta] { + &self.tx_data.outputs + } + + pub fn current_script_hash(&self) -> &Byte32 { + &self.script_hash + } } impl
DataSource for Arc> @@ -919,51 +943,6 @@ where } } -/// Immutable context data at virtual machine level -#[derive(Clone, Debug)] -pub struct VmData
{ - /// Script group level data - pub sg_data: Arc>, - - /// Currently executed virtual machine ID - pub vm_id: VmId, -} - -impl
VmData
{ - pub fn rtx(&self) -> &ResolvedTransaction { - &self.sg_data.tx_data.rtx - } - - pub fn data_loader(&self) -> &DL { - &self.sg_data.tx_data.data_loader - } - - pub fn group_inputs(&self) -> &[usize] { - &self.sg_data.script_group.input_indices - } - - pub fn group_outputs(&self) -> &[usize] { - &self.sg_data.script_group.output_indices - } - - pub fn outputs(&self) -> &[CellMeta] { - &self.sg_data.tx_data.outputs - } - - pub fn current_script_hash(&self) -> &Byte32 { - &self.sg_data.script_hash - } -} - -impl
DataSource for Arc> -where - DL: CellDataProvider, -{ - fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> { - self.sg_data.load_data(id, offset, length) - } -} - /// Mutable data at virtual machine level #[derive(Clone)] pub struct VmContext
@@ -973,7 +952,7 @@ where pub(crate) base_cycles: Arc, /// A mutable reference to scheduler's message box pub(crate) message_box: Arc>>, - pub(crate) snapshot2_context: Arc>>>>, + pub(crate) snapshot2_context: Arc>>>>, } impl
VmContext
@@ -983,11 +962,11 @@ where /// Creates a new VM context. It is by design that parameters to this function /// are references. It is a reminder that the inputs are designed to be shared /// among different entities. - pub fn new(vm_data: &Arc>, message_box: &Arc>>) -> Self { + pub fn new(sg_data: &Arc>, message_box: &Arc>>) -> Self { Self { base_cycles: Arc::new(AtomicU64::new(0)), message_box: Arc::clone(message_box), - snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(vm_data)))), + snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(sg_data)))), } } From 8b6654ab743d7f10a86c2a4db338b10ddbad6504 Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Thu, 13 Feb 2025 05:48:10 +0000 Subject: [PATCH 8/8] perf: Fix a performance regression caused by the refactoring work In the original refactoring work, the verifying context data is kept in `Arc` structure, which contains `Arc`, which then contains `Arc`. Accessing a specific field on a CKB transaction requires traversing through 3 `Arc` structures, which would be 3 levels of indirection. Benchmarks show that those nested memory indirections cost us ~3% of transaction verifying performance, our best guess is that the nested indirections result in enough cache misses to cause the differences. This commit changes the code so instead of nested `Arc`s, we now use flattened structs that then contain a series of `Arc`s. Now at most one `Arc` would get in the way to access a specific field of the CKB transaction to verify. Note that `consensus` and `tx_env` still hids in 2 nested levels of `Arc`, here we are balancing the nested levels of `Arc`, and the number of `Arc`s contained in the flattend `TxData` and `SgData` structure. Right now we work under the assumption that if consensus parameters are required indeed, a nested level of indirection won't be the bottleneck. However, this might or might not change in the future, when it becomes a problem, we will revisit the tradeoff then. --- script/src/scheduler.rs | 14 +- script/src/syscalls/debugger.rs | 18 +- script/src/syscalls/exec.rs | 17 +- script/src/syscalls/generator.rs | 11 +- script/src/syscalls/load_block_extension.rs | 17 +- script/src/syscalls/load_cell.rs | 29 +-- script/src/syscalls/load_cell_data.rs | 2 +- script/src/syscalls/load_header.rs | 24 +-- script/src/syscalls/load_input.rs | 13 +- script/src/syscalls/load_script.rs | 16 +- script/src/syscalls/load_script_hash.rs | 16 +- script/src/syscalls/load_tx.rs | 4 +- script/src/syscalls/load_witness.rs | 13 +- script/src/syscalls/spawn.rs | 2 +- script/src/syscalls/tests/utils.rs | 79 ++++++--- .../syscalls/tests/vm_latest/syscalls_1.rs | 163 ++++++----------- .../syscalls/tests/vm_latest/syscalls_2.rs | 6 +- script/src/types.rs | 165 ++++++++++++------ 18 files changed, 319 insertions(+), 290 deletions(-) diff --git a/script/src/scheduler.rs b/script/src/scheduler.rs index 4ace66ab37..348b5caeaa 100644 --- a/script/src/scheduler.rs +++ b/script/src/scheduler.rs @@ -48,7 +48,7 @@ where DL: CellDataProvider, { /// Immutable context data for current running transaction & script. - pub sg_data: Arc>, + pub sg_data: SgData
, /// Mutable context data used by current scheduler pub debug_context: DebugContext, @@ -113,7 +113,7 @@ where /// Create a new scheduler from empty state pub fn new(sg_data: SgData
, debug_context: DebugContext) -> Self { Self { - sg_data: Arc::new(sg_data), + sg_data, debug_context, total_cycles: Arc::new(AtomicU64::new(0)), iteration_cycles: 0, @@ -153,7 +153,7 @@ where full: FullSuspendedState, ) -> Self { let mut scheduler = Self { - sg_data: Arc::new(sg_data), + sg_data, debug_context, total_cycles: Arc::new(AtomicU64::new(full.total_cycles)), iteration_cycles: 0, @@ -232,7 +232,7 @@ where pub fn run(&mut self, mode: RunMode) -> Result<(i8, Cycle), Error> { if self.states.is_empty() { // Booting phase, we will need to initialize the first VM. - let program_id = self.sg_data.program_data_piece_id.clone(); + let program_id = self.sg_data.sg_info.program_data_piece_id.clone(); assert_eq!( self.boot_vm( &DataLocation { @@ -877,7 +877,7 @@ where // The code here looks slightly weird, since I don't want to copy over all syscall // impls here again. Ideally, this scheduler package should be merged with ckb-script, // or simply replace ckb-script. That way, the quirks here will be eliminated. - let version = &self.sg_data.script_version; + let version = &self.sg_data.sg_info.script_version; let core_machine = CoreMachineType::new( version.vm_isa(), version.vm_version(), @@ -887,9 +887,7 @@ where let vm_context = VmContext { base_cycles: Arc::clone(&self.total_cycles), message_box: Arc::clone(&self.message_box), - snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone( - &self.sg_data, - )))), + snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(self.sg_data.clone()))), }; let machine_builder = DefaultMachineBuilder::new(core_machine) diff --git a/script/src/syscalls/debugger.rs b/script/src/syscalls/debugger.rs index d71732303e..75d2771638 100644 --- a/script/src/syscalls/debugger.rs +++ b/script/src/syscalls/debugger.rs @@ -1,4 +1,6 @@ -use crate::types::{DebugContext, DebugPrinter, SgData}; +use crate::types::{ + DebugContext, DebugPrinter, {SgData, SgInfo}, +}; use crate::{cost_model::transferred_byte_cycles, syscalls::DEBUG_PRINT_SYSCALL_NUMBER}; use ckb_vm::{ registers::{A0, A7}, @@ -6,21 +8,21 @@ use ckb_vm::{ }; use std::sync::Arc; -pub struct Debugger
{ - sg_data: Arc>, +pub struct Debugger { + sg_info: Arc, printer: DebugPrinter, } -impl
Debugger
{ - pub fn new(sg_data: &Arc>, debug_context: &DebugContext) -> Debugger
{ +impl Debugger { + pub fn new
(sg_data: &SgData
, debug_context: &DebugContext) -> Debugger { Debugger { - sg_data: Arc::clone(sg_data), + sg_info: Arc::clone(&sg_data.sg_info), printer: Arc::clone(&debug_context.debug_printer), } } } -impl Syscalls for Debugger
{ +impl Syscalls for Debugger { fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } @@ -49,7 +51,7 @@ impl Syscalls for Debugger
{ machine.add_cycles_no_checking(transferred_byte_cycles(buffer.len() as u64))?; let s = String::from_utf8(buffer) .map_err(|e| VMError::External(format!("String from buffer {e:?}")))?; - (self.printer)(self.sg_data.current_script_hash(), s.as_str()); + (self.printer)(&self.sg_info.script_hash, s.as_str()); Ok(true) } diff --git a/script/src/syscalls/exec.rs b/script/src/syscalls/exec.rs index 97140104fd..ac1ce8cb70 100644 --- a/script/src/syscalls/exec.rs +++ b/script/src/syscalls/exec.rs @@ -15,33 +15,32 @@ use ckb_vm::{ Error as VMError, Register, SupportMachine, Syscalls, }; use ckb_vm::{DEFAULT_STACK_SIZE, RISCV_MAX_MEMORY}; -use std::sync::Arc; #[derive(Debug)] pub struct Exec
{ - sg_data: Arc>, + sg_data: SgData
, } -impl Exec
{ - pub fn new(sg_data: &Arc>) -> Exec
{ +impl Exec
{ + pub fn new(sg_data: &SgData
) -> Exec
{ Exec { - sg_data: Arc::clone(sg_data), + sg_data: sg_data.clone(), } } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.sg_data.rtx().resolved_inputs + &self.sg_data.rtx.resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.sg_data.rtx().resolved_cell_deps + &self.sg_data.rtx.resolved_cell_deps } #[inline] fn witnesses(&self) -> BytesVec { - self.sg_data.rtx().transaction.witnesses() + self.sg_data.rtx.transaction.witnesses() } fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { @@ -92,7 +91,7 @@ impl Exec
{ } } -impl Syscalls for Exec
{ +impl Syscalls for Exec
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/generator.rs b/script/src/syscalls/generator.rs index c57adb8629..98e8e7c1d5 100644 --- a/script/src/syscalls/generator.rs +++ b/script/src/syscalls/generator.rs @@ -8,12 +8,11 @@ use crate::{ }; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::Syscalls; -use std::sync::Arc; /// Generate RISC-V syscalls in CKB environment pub fn generate_ckb_syscalls
( vm_id: &VmId, - sg_data: &Arc>, + sg_data: &SgData
, vm_context: &VmContext
, debug_context: &DebugContext, ) -> Vec)>> @@ -31,7 +30,7 @@ where Box::new(LoadCellData::new(vm_context)), Box::new(Debugger::new(sg_data, debug_context)), ]; - let script_version = &sg_data.script_version; + let script_version = &sg_data.sg_info.script_version; if script_version >= &ScriptVersion::V1 { syscalls.append(&mut vec![ Box::new(VMVersion::new()), @@ -56,8 +55,8 @@ where ]); } #[cfg(test)] - syscalls.push(Box::new(crate::syscalls::Pause::new(Arc::clone( - &debug_context.skip_pause, - )))); + syscalls.push(Box::new(crate::syscalls::Pause::new( + std::sync::Arc::clone(&debug_context.skip_pause), + ))); syscalls } diff --git a/script/src/syscalls/load_block_extension.rs b/script/src/syscalls/load_block_extension.rs index 0594a890e3..d38cf4b8cc 100644 --- a/script/src/syscalls/load_block_extension.rs +++ b/script/src/syscalls/load_block_extension.rs @@ -15,33 +15,32 @@ use ckb_vm::{ registers::{A0, A3, A4, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; #[derive(Debug)] pub struct LoadBlockExtension
{ - sg_data: Arc>, + sg_data: SgData
, } -impl LoadBlockExtension
{ - pub fn new(sg_data: &Arc>) -> LoadBlockExtension
{ +impl LoadBlockExtension
{ + pub fn new(sg_data: &SgData
) -> LoadBlockExtension
{ LoadBlockExtension { - sg_data: Arc::clone(sg_data), + sg_data: sg_data.clone(), } } #[inline] fn header_deps(&self) -> Byte32Vec { - self.sg_data.rtx().transaction.header_deps() + self.sg_data.rtx.transaction.header_deps() } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.sg_data.rtx().resolved_inputs + &self.sg_data.rtx.resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.sg_data.rtx().resolved_cell_deps + &self.sg_data.rtx.resolved_cell_deps } fn load_block_extension(&self, cell_meta: &CellMeta) -> Option { @@ -102,7 +101,7 @@ impl LoadBlockExtension
{ } } -impl Syscalls +impl Syscalls for LoadBlockExtension
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { diff --git a/script/src/syscalls/load_cell.rs b/script/src/syscalls/load_cell.rs index 78260c378b..3c6212b1bd 100644 --- a/script/src/syscalls/load_cell.rs +++ b/script/src/syscalls/load_cell.rs @@ -4,7 +4,7 @@ use crate::{ utils::store_data, CellField, Source, SourceEntry, INDEX_OUT_OF_BOUND, ITEM_MISSING, LOAD_CELL_BY_FIELD_SYSCALL_NUMBER, LOAD_CELL_SYSCALL_NUMBER, SUCCESS, }, - types::{SgData, TxData}, + types::{SgData, TxInfo}, }; use byteorder::{LittleEndian, WriteBytesExt}; use ckb_traits::CellDataProvider; @@ -17,32 +17,31 @@ use ckb_vm::{ registers::{A0, A3, A4, A5, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; pub struct LoadCell
{ - sg_data: Arc>, + sg_data: SgData
, } -impl LoadCell
{ - pub fn new(sg_data: &Arc>) -> LoadCell
{ +impl LoadCell
{ + pub fn new(sg_data: &SgData
) -> LoadCell
{ LoadCell { - sg_data: Arc::clone(sg_data), + sg_data: sg_data.clone(), } } #[inline] - fn tx_data(&self) -> &TxData
{ - &self.sg_data.tx_data + fn tx_info(&self) -> &TxInfo
{ + &self.sg_data.tx_info } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.sg_data.rtx().resolved_inputs + &self.sg_data.rtx.resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.sg_data.rtx().resolved_cell_deps + &self.sg_data.rtx.resolved_cell_deps } fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { @@ -51,7 +50,7 @@ impl LoadCell
{ self.resolved_inputs().get(index).ok_or(INDEX_OUT_OF_BOUND) } Source::Transaction(SourceEntry::Output) => { - self.tx_data().outputs.get(index).ok_or(INDEX_OUT_OF_BOUND) + self.tx_info().outputs.get(index).ok_or(INDEX_OUT_OF_BOUND) } Source::Transaction(SourceEntry::CellDep) => self .resolved_cell_deps() @@ -74,7 +73,7 @@ impl LoadCell
{ .get(index) .ok_or(INDEX_OUT_OF_BOUND) .and_then(|actual_index| { - self.tx_data() + self.tx_info() .outputs .get(*actual_index) .ok_or(INDEX_OUT_OF_BOUND) @@ -110,7 +109,7 @@ impl LoadCell
{ (SUCCESS, store_data(machine, &buffer)?) } CellField::DataHash => { - if let Some(bytes) = self.tx_data().data_loader.load_cell_data_hash(cell) { + if let Some(bytes) = self.tx_info().data_loader.load_cell_data_hash(cell) { (SUCCESS, store_data(machine, &bytes.as_bytes())?) } else { (ITEM_MISSING, 0) @@ -160,7 +159,9 @@ impl LoadCell
{ } } -impl Syscalls for LoadCell
{ +impl Syscalls + for LoadCell
+{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/load_cell_data.rs b/script/src/syscalls/load_cell_data.rs index 53b14b0d1c..3aa6b24645 100644 --- a/script/src/syscalls/load_cell_data.rs +++ b/script/src/syscalls/load_cell_data.rs @@ -20,7 +20,7 @@ pub struct LoadCellData
where DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, { - snapshot2_context: Arc>>>>, + snapshot2_context: Arc>>>, } impl
LoadCellData
diff --git a/script/src/syscalls/load_header.rs b/script/src/syscalls/load_header.rs index 5e503e3c1d..10629c844c 100644 --- a/script/src/syscalls/load_header.rs +++ b/script/src/syscalls/load_header.rs @@ -17,17 +17,15 @@ use ckb_vm::{ registers::{A0, A3, A4, A5, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; - #[derive(Debug)] pub struct LoadHeader
{ - sg_data: Arc>, + sg_data: SgData
, } -impl LoadHeader
{ - pub fn new(sg_data: &Arc>) -> LoadHeader
{ +impl LoadHeader
{ + pub fn new(sg_data: &SgData
) -> LoadHeader
{ LoadHeader { - sg_data: Arc::clone(sg_data), + sg_data: sg_data.clone(), } } @@ -42,17 +40,17 @@ impl LoadHeader
{ #[inline] fn header_deps(&self) -> Byte32Vec { - self.sg_data.rtx().transaction.header_deps() + self.sg_data.rtx.transaction.header_deps() } #[inline] fn resolved_inputs(&self) -> &Vec { - &self.sg_data.rtx().resolved_inputs + &self.sg_data.rtx.resolved_inputs } #[inline] fn resolved_cell_deps(&self) -> &Vec { - &self.sg_data.rtx().resolved_cell_deps + &self.sg_data.rtx.resolved_cell_deps } fn load_header(&self, cell_meta: &CellMeta) -> Option { @@ -66,7 +64,7 @@ impl LoadHeader
{ .into_iter() .any(|hash| &hash == block_hash) { - self.sg_data.tx_data.data_loader.get_header(block_hash) + self.sg_data.tx_info.data_loader.get_header(block_hash) } else { None } @@ -91,7 +89,7 @@ impl LoadHeader
{ .ok_or(INDEX_OUT_OF_BOUND) .and_then(|block_hash| { self.sg_data - .tx_data + .tx_info .data_loader .get_header(&block_hash) .ok_or(ITEM_MISSING) @@ -146,7 +144,9 @@ impl LoadHeader
{ } } -impl Syscalls for LoadHeader
{ +impl Syscalls + for LoadHeader
+{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/load_input.rs b/script/src/syscalls/load_input.rs index 5d0513a45e..2d1f415f32 100644 --- a/script/src/syscalls/load_input.rs +++ b/script/src/syscalls/load_input.rs @@ -15,23 +15,22 @@ use ckb_vm::{ registers::{A0, A3, A4, A5, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; #[derive(Debug)] pub struct LoadInput
{ - sg_data: Arc>, + sg_data: SgData
, } -impl
LoadInput
{ - pub fn new(sg_data: &Arc>) -> Self { +impl LoadInput
{ + pub fn new(sg_data: &SgData
) -> Self { LoadInput { - sg_data: Arc::clone(sg_data), + sg_data: sg_data.clone(), } } #[inline] fn inputs(&self) -> CellInputVec { - self.sg_data.rtx().transaction.inputs() + self.sg_data.rtx.transaction.inputs() } fn fetch_input(&self, source: Source, index: usize) -> Result { @@ -88,7 +87,7 @@ impl
LoadInput
{ } } -impl Syscalls for LoadInput
{ +impl Syscalls for LoadInput
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/load_script.rs b/script/src/syscalls/load_script.rs index 6ea2ec8d32..545e8e7492 100644 --- a/script/src/syscalls/load_script.rs +++ b/script/src/syscalls/load_script.rs @@ -1,7 +1,7 @@ use crate::{ cost_model::transferred_byte_cycles, syscalls::{utils::store_data, LOAD_SCRIPT_SYSCALL_NUMBER, SUCCESS}, - types::SgData, + types::{SgData, SgInfo}, }; use ckb_types::prelude::*; use ckb_vm::{ @@ -11,19 +11,19 @@ use ckb_vm::{ use std::sync::Arc; #[derive(Debug)] -pub struct LoadScript
{ - sg_data: Arc>, +pub struct LoadScript { + sg_info: Arc, } -impl
LoadScript
{ - pub fn new(sg_data: &Arc>) -> Self { +impl LoadScript { + pub fn new
(sg_data: &SgData
) -> Self { Self { - sg_data: Arc::clone(sg_data), + sg_info: Arc::clone(&sg_data.sg_info), } } } -impl Syscalls for LoadScript
{ +impl Syscalls for LoadScript { fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } @@ -33,7 +33,7 @@ impl Syscalls for LoadScript
{ return Ok(false); } - let data = self.sg_data.script_group.script.as_slice(); + let data = self.sg_info.script_group.script.as_slice(); let wrote_size = store_data(machine, data)?; machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; diff --git a/script/src/syscalls/load_script_hash.rs b/script/src/syscalls/load_script_hash.rs index 73ecefa88e..0fe93e2f30 100644 --- a/script/src/syscalls/load_script_hash.rs +++ b/script/src/syscalls/load_script_hash.rs @@ -1,7 +1,7 @@ use crate::{ cost_model::transferred_byte_cycles, syscalls::{utils::store_data, LOAD_SCRIPT_HASH_SYSCALL_NUMBER, SUCCESS}, - types::SgData, + types::{SgData, SgInfo}, }; use ckb_vm::{ registers::{A0, A7}, @@ -10,19 +10,19 @@ use ckb_vm::{ use std::sync::Arc; #[derive(Debug)] -pub struct LoadScriptHash
{ - sg_data: Arc>, +pub struct LoadScriptHash { + sg_info: Arc, } -impl
LoadScriptHash
{ - pub fn new(sg_data: &Arc>) -> LoadScriptHash
{ +impl LoadScriptHash { + pub fn new
(sg_data: &SgData
) -> LoadScriptHash { LoadScriptHash { - sg_data: Arc::clone(sg_data), + sg_info: Arc::clone(&sg_data.sg_info), } } } -impl Syscalls for LoadScriptHash
{ +impl Syscalls for LoadScriptHash { fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } @@ -32,7 +32,7 @@ impl Syscalls for LoadScriptHash
return Ok(false); } - let data = self.sg_data.current_script_hash().as_reader().raw_data(); + let data = self.sg_info.script_hash.as_reader().raw_data(); let wrote_size = store_data(machine, data)?; machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; diff --git a/script/src/syscalls/load_tx.rs b/script/src/syscalls/load_tx.rs index fb4576682d..d0db1a3ce0 100644 --- a/script/src/syscalls/load_tx.rs +++ b/script/src/syscalls/load_tx.rs @@ -18,9 +18,9 @@ pub struct LoadTx { } impl LoadTx { - pub fn new
(sg_data: &Arc>) -> LoadTx { + pub fn new
(sg_data: &SgData
) -> LoadTx { LoadTx { - rtx: Arc::clone(&sg_data.tx_data.rtx), + rtx: Arc::clone(&sg_data.rtx), } } } diff --git a/script/src/syscalls/load_witness.rs b/script/src/syscalls/load_witness.rs index 09e1698db3..3ff4863199 100644 --- a/script/src/syscalls/load_witness.rs +++ b/script/src/syscalls/load_witness.rs @@ -11,23 +11,22 @@ use ckb_vm::{ registers::{A0, A3, A4, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; #[derive(Debug)] pub struct LoadWitness
{ - sg_data: Arc>, + sg_data: SgData
, } -impl
LoadWitness
{ - pub fn new(sg_data: &Arc>) -> Self { +impl LoadWitness
{ + pub fn new(sg_data: &SgData
) -> Self { LoadWitness { - sg_data: Arc::clone(sg_data), + sg_data: sg_data.clone(), } } #[inline] fn witnesses(&self) -> BytesVec { - self.sg_data.rtx().transaction.witnesses() + self.sg_data.rtx.transaction.witnesses() } fn fetch_witness(&self, source: Source, index: usize) -> Option { @@ -49,7 +48,7 @@ impl
LoadWitness
{ } } -impl Syscalls for LoadWitness
{ +impl Syscalls for LoadWitness
{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/spawn.rs b/script/src/syscalls/spawn.rs index 6fb7d536eb..a90ed3082a 100644 --- a/script/src/syscalls/spawn.rs +++ b/script/src/syscalls/spawn.rs @@ -20,7 +20,7 @@ where { id: VmId, message_box: Arc>>, - snapshot2_context: Arc>>>>, + snapshot2_context: Arc>>>, } impl
Spawn
diff --git a/script/src/syscalls/tests/utils.rs b/script/src/syscalls/tests/utils.rs index 6d5d95330b..4f2da451e9 100644 --- a/script/src/syscalls/tests/utils.rs +++ b/script/src/syscalls/tests/utils.rs @@ -1,5 +1,7 @@ use crate::{ - types::{DataPieceId, ScriptGroup, ScriptGroupType, ScriptVersion, SgData, TxData}, + types::{ + DataPieceId, ScriptGroup, ScriptGroupType, ScriptVersion, SgData, SgInfo, TxData, TxInfo, + }, verify_env::TxVerifyEnv, }; use ckb_chain_spec::consensus::ConsensusBuilder; @@ -62,11 +64,7 @@ pub(crate) fn build_cell_meta(capacity_bytes: usize, data: Bytes) -> CellMeta { } } -pub(crate) fn build_tx_data(rtx: Arc) -> TxData { - build_tx_data_with_loader(rtx, new_mock_data_loader()) -} - -pub(crate) fn build_tx_data_with_loader( +fn build_tx_data_with_loader( rtx: Arc, data_loader: MockDataLoader, ) -> TxData { @@ -75,22 +73,34 @@ pub(crate) fn build_tx_data_with_loader( TxData { rtx, - data_loader, - consensus: Arc::new(consensus), - tx_env: Arc::new(tx_env), - binaries_by_data_hash: HashMap::default(), - binaries_by_type_hash: HashMap::default(), - lock_groups: BTreeMap::default(), - type_groups: BTreeMap::default(), - outputs: Vec::new(), + info: Arc::new(TxInfo { + data_loader, + consensus: Arc::new(consensus), + tx_env: Arc::new(tx_env), + binaries_by_data_hash: HashMap::default(), + binaries_by_type_hash: HashMap::default(), + lock_groups: BTreeMap::default(), + type_groups: BTreeMap::default(), + outputs: Vec::new(), + }), } } pub(crate) fn build_sg_data( - tx_data: Arc>, + rtx: Arc, input_indices: Vec, output_indices: Vec, -) -> Arc> { +) -> SgData { + build_sg_data_with_loader(rtx, new_mock_data_loader(), input_indices, output_indices) +} + +pub(crate) fn build_sg_data_with_loader( + rtx: Arc, + data_loader: MockDataLoader, + input_indices: Vec, + output_indices: Vec, +) -> SgData { + let tx_data = build_tx_data_with_loader(rtx, data_loader); let script_group = ScriptGroup { script: Script::default(), group_type: ScriptGroupType::Lock, @@ -98,11 +108,34 @@ pub(crate) fn build_sg_data( output_indices, }; let script_hash = script_group.script.calc_script_hash(); - Arc::new(SgData { - tx_data, - script_version: ScriptVersion::latest(), - script_group, - script_hash, - program_data_piece_id: DataPieceId::CellDep(0), - }) + SgData { + rtx: tx_data.rtx, + tx_info: tx_data.info, + sg_info: Arc::new(SgInfo { + script_version: ScriptVersion::latest(), + script_group, + script_hash, + program_data_piece_id: DataPieceId::CellDep(0), + }), + } +} + +pub(crate) fn update_tx_info)>( + mut sg_data: SgData, + f: F, +) -> SgData { + let mut tx_info = sg_data.tx_info.as_ref().clone(); + f(&mut tx_info); + sg_data.tx_info = Arc::new(tx_info); + sg_data +} + +pub(crate) fn update_sg_info( + mut sg_data: SgData, + f: F, +) -> SgData { + let mut sg_info = sg_data.sg_info.as_ref().clone(); + f(&mut sg_info); + sg_data.sg_info = Arc::new(sg_info); + sg_data } diff --git a/script/src/syscalls/tests/vm_latest/syscalls_1.rs b/script/src/syscalls/tests/vm_latest/syscalls_1.rs index 444a67a124..98f7843dcc 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_1.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_1.rs @@ -53,13 +53,8 @@ fn _test_load_cell_not_exist(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); + let sg_data = update_tx_info(sg_data, |tx_info| tx_info.outputs = vec![output.clone()]); let mut load_cell = LoadCell::new(&sg_data); @@ -99,13 +94,8 @@ fn _test_load_cell_all(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output.clone()]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); + let sg_data = update_tx_info(sg_data, |tx_info| tx_info.outputs = vec![output.clone()]); let mut load_cell = LoadCell::new(&sg_data); @@ -193,13 +183,8 @@ fn _test_load_cell_from_group(data: &[u8], source: SourceEntry) -> Result<(), Te resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output.clone()]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(rtx, vec![0], vec![0]); + let sg_data = update_tx_info(sg_data, |tx_info| tx_info.outputs = vec![output.clone()]); let mut load_cell = LoadCell::new(&sg_data); @@ -284,13 +269,8 @@ fn _test_load_cell_out_of_bound(index: u64, source: u64) -> Result<(), TestCaseE resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(rtx, vec![0], vec![0]); + let sg_data = update_tx_info(sg_data, |tx_info| tx_info.outputs = vec![output.clone()]); let mut load_cell = LoadCell::new(&sg_data); @@ -346,13 +326,8 @@ fn _test_load_cell_length(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); + let sg_data = update_tx_info(sg_data, |tx_info| tx_info.outputs = vec![output.clone()]); let mut load_cell = LoadCell::new(&sg_data); @@ -402,13 +377,8 @@ fn _test_load_cell_partial(data: &[u8], offset: u64) -> Result<(), TestCaseError resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); + let sg_data = update_tx_info(sg_data, |tx_info| tx_info.outputs = vec![output.clone()]); let mut load_cell = LoadCell::new(&sg_data); @@ -471,8 +441,7 @@ fn _test_load_cell_capacity(capacity: Capacity) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -530,8 +499,7 @@ fn _test_load_cell_occupied_capacity(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -590,8 +558,7 @@ fn test_load_missing_data_hash() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -635,13 +602,10 @@ fn _test_load_missing_contract(field: CellField) { resolved_dep_groups: vec![], }); - let tx_data = { - // Mutable TxData should only used in tests - let mut tx_data = build_tx_data(rtx); - tx_data.outputs = vec![output_cell]; - Arc::new(tx_data) - }; - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); + let sg_data = update_tx_info(sg_data, |tx_info| { + tx_info.outputs = vec![output_cell.clone()] + }); let mut load_cell = LoadCell::new(&sg_data); @@ -712,8 +676,7 @@ fn _test_load_header( resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); - let sg_data = build_sg_data(tx_data, vec![0], vec![]); + let sg_data = build_sg_data_with_loader(rtx, data_loader, vec![0], vec![]); let mut load_header = LoadHeader::new(&sg_data); @@ -830,8 +793,7 @@ fn _test_load_header_by_field(data: &[u8], field: HeaderField) -> Result<(), Tes resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data_with_loader(rtx, data_loader, vec![], vec![]); let mut load_header = LoadHeader::new(&sg_data); @@ -883,8 +845,7 @@ fn _test_load_tx_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_tx = LoadTx::new(&sg_data); @@ -937,8 +898,7 @@ fn _test_load_tx(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_tx = LoadTx::new(&sg_data); @@ -992,15 +952,12 @@ fn _test_load_current_script_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); // Swap the internal script in VmData - let sg_data = { - let mut sg_data = sg_data.as_ref().clone(); - sg_data.script_hash = script.calc_script_hash(); - sg_data.script_group.script = script; - Arc::new(sg_data) - }; + let sg_data = update_sg_info(sg_data, |sg_info| { + sg_info.script_hash = script.calc_script_hash(); + sg_info.script_group.script = script.clone(); + }); let mut load_script_hash = LoadScriptHash::new(&sg_data); @@ -1074,8 +1031,7 @@ fn _test_load_input_lock_script_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -1137,8 +1093,7 @@ fn _test_load_input_lock_script(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -1203,8 +1158,7 @@ fn _test_load_input_type_script(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -1271,8 +1225,7 @@ fn _test_load_input_type_script_hash(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_cell = LoadCell::new(&sg_data); @@ -1328,8 +1281,7 @@ fn _test_load_witness(data: &[u8], source: SourceEntry) -> Result<(), TestCaseEr resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let mut load_witness = LoadWitness::new(&sg_data); @@ -1394,8 +1346,7 @@ fn _test_load_group_witness(data: &[u8], source: SourceEntry) -> Result<(), Test resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![1], vec![1]); + let sg_data = build_sg_data(rtx, vec![1], vec![1]); let mut load_witness = LoadWitness::new(&sg_data); @@ -1451,15 +1402,12 @@ fn _test_load_script(data: &[u8]) -> Result<(), TestCaseError> { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); // Swap the internal script in VmData - let sg_data = { - let mut sg_data = sg_data.as_ref().clone(); - sg_data.script_hash = script.calc_script_hash(); - sg_data.script_group.script = script.clone(); - Arc::new(sg_data) - }; + let sg_data = update_sg_info(sg_data, |sg_info| { + sg_info.script_hash = script.calc_script_hash(); + sg_info.script_group.script = script.clone(); + }); let mut load_script = LoadScript::new(&sg_data); @@ -1524,8 +1472,7 @@ fn _test_load_cell_data_as_code( resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(rtx, vec![0], vec![0]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1592,8 +1539,7 @@ fn _test_load_cell_data( resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(rtx, vec![0], vec![0]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1695,8 +1641,7 @@ fn test_load_overflowed_cell_data_as_code() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1740,8 +1685,7 @@ fn _test_load_cell_data_on_freezed_memory(data: &[u8]) -> Result<(), TestCaseErr resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1783,8 +1727,7 @@ fn _test_load_cell_data_as_code_on_freezed_memory(data: &[u8]) -> Result<(), Tes resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1837,8 +1780,7 @@ fn test_load_code_unaligned_error() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1878,8 +1820,7 @@ fn test_load_code_slice_out_of_bound_error() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1922,8 +1863,7 @@ fn test_load_code_not_enough_space_error() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -1989,8 +1929,7 @@ fn _test_load_input( resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![0], vec![]); + let sg_data = build_sg_data(rtx, vec![0], vec![]); let mut load_input = LoadInput::new(&sg_data); @@ -2129,8 +2068,7 @@ fn test_load_cell_data_size_zero() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(rtx, vec![0], vec![0]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -2169,8 +2107,7 @@ fn test_load_cell_data_size_zero_index_out_of_bound() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![0], vec![0]); + let sg_data = build_sg_data(rtx, vec![0], vec![0]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); diff --git a/script/src/syscalls/tests/vm_latest/syscalls_2.rs b/script/src/syscalls/tests/vm_latest/syscalls_2.rs index 38b3f7c9ef..0f40dc1e51 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_2.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_2.rs @@ -60,8 +60,7 @@ fn test_current_cycles() { resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data(rtx)); - let sg_data = build_sg_data(tx_data, vec![], vec![]); + let sg_data = build_sg_data(rtx, vec![], vec![]); let vm_context = VmContext::new(&sg_data, &Arc::new(Mutex::new(Vec::new()))); @@ -121,8 +120,7 @@ fn _test_load_extension( resolved_dep_groups: vec![], }); - let tx_data = Arc::new(build_tx_data_with_loader(rtx, data_loader)); - let sg_data = build_sg_data(tx_data, vec![0], vec![]); + let sg_data = build_sg_data_with_loader(rtx, data_loader, vec![0], vec![]); let mut load_block_extension = LoadBlockExtension::new(&sg_data); diff --git a/script/src/types.rs b/script/src/types.rs index eee70ab348..a7c9cdfbac 100644 --- a/script/src/types.rs +++ b/script/src/types.rs @@ -564,6 +564,15 @@ impl Binaries { pub struct TxData
{ /// ResolvedTransaction. pub rtx: Arc, + + /// Passed & derived information. + pub info: Arc>, +} + +/// Information that is either passed as the context of the transaction, +/// or can be derived from the transaction. +#[derive(Clone, Debug)] +pub struct TxInfo
{ /// Data loader. pub data_loader: DL, /// Chain consensus parameters @@ -663,17 +672,30 @@ where Self { rtx, - data_loader, - consensus, - tx_env, - binaries_by_data_hash, - binaries_by_type_hash, - lock_groups, - type_groups, - outputs, + info: Arc::new(TxInfo { + data_loader, + consensus, + tx_env, + binaries_by_data_hash, + binaries_by_type_hash, + lock_groups, + type_groups, + outputs, + }), } } + #[inline] + /// Extracts actual script binary either in dep cells. + pub fn extract_script(&self, script: &Script) -> Result { + self.info.extract_script(script) + } +} + +impl
TxInfo
+where + DL: CellDataProvider, +{ #[inline] /// Extracts actual script binary either in dep cells. pub fn extract_script(&self, script: &Script) -> Result { @@ -683,6 +705,50 @@ where } impl
TxData
{ + #[inline] + /// Calculates transaction hash + pub fn tx_hash(&self) -> Byte32 { + self.rtx.transaction.hash() + } + + #[inline] + /// Extracts the index of the script binary in dep cells + pub fn extract_referenced_dep_index(&self, script: &Script) -> Result { + self.info.extract_referenced_dep_index(script) + } + + #[inline] + /// Finds the script group from cell deps. + pub fn find_script_group( + &self, + script_group_type: ScriptGroupType, + script_hash: &Byte32, + ) -> Option<&ScriptGroup> { + self.info.find_script_group(script_group_type, script_hash) + } + + #[inline] + /// Returns the version of the machine based on the script and the consensus rules. + pub fn select_version(&self, script: &Script) -> Result { + self.info.select_version(script) + } + + #[inline] + /// Returns all script groups. + pub fn groups(&self) -> impl Iterator { + self.info.groups() + } + + #[inline] + /// Returns all script groups with type. + pub fn groups_with_type( + &self, + ) -> impl Iterator { + self.info.groups_with_type() + } +} + +impl
TxInfo
{ #[inline] /// Extracts the index of the script binary in dep cells pub fn extract_referenced_dep_index(&self, script: &Script) -> Result { @@ -719,12 +785,6 @@ impl
TxData
{ } } - #[inline] - /// Calculates transaction hash - pub fn tx_hash(&self) -> Byte32 { - self.rtx.transaction.hash() - } - /// Finds the script group from cell deps. pub fn find_script_group( &self, @@ -818,9 +878,19 @@ impl
TxData
{ /// Immutable context data at script group level #[derive(Clone, Debug)] pub struct SgData
{ - /// Transaction level data - pub tx_data: Arc>, + /// ResolvedTransaction. + pub rtx: Arc, + + /// Passed & derived information at transaction level. + pub tx_info: Arc>, + /// Passed & derived information at script group level. + pub sg_info: Arc, +} + +/// Script group level derived information. +#[derive(Clone, Debug)] +pub struct SgInfo { /// Currently executed script version pub script_version: ScriptVersion, /// Currently executed script group @@ -832,7 +902,7 @@ pub struct SgData
{ } impl
SgData
{ - pub fn new(tx_data: &Arc>, script_group: &ScriptGroup) -> Result { + pub fn new(tx_data: &TxData
, script_group: &ScriptGroup) -> Result { let script_hash = script_group.script.calc_script_hash(); let script_version = tx_data.select_version(&script_group.script)?; let dep_index = tx_data @@ -840,94 +910,89 @@ impl
SgData
{ .try_into() .map_err(|_| ScriptError::Other("u32 overflow".to_string()))?; Ok(Self { - tx_data: Arc::clone(tx_data), - script_version, - script_hash, - script_group: script_group.clone(), - program_data_piece_id: DataPieceId::CellDep(dep_index), + rtx: Arc::clone(&tx_data.rtx), + tx_info: Arc::clone(&tx_data.info), + sg_info: Arc::new(SgInfo { + script_version, + script_hash, + script_group: script_group.clone(), + program_data_piece_id: DataPieceId::CellDep(dep_index), + }), }) } - pub fn rtx(&self) -> &ResolvedTransaction { - &self.tx_data.rtx - } - pub fn data_loader(&self) -> &DL { - &self.tx_data.data_loader + &self.tx_info.data_loader } pub fn group_inputs(&self) -> &[usize] { - &self.script_group.input_indices + &self.sg_info.script_group.input_indices } pub fn group_outputs(&self) -> &[usize] { - &self.script_group.output_indices + &self.sg_info.script_group.output_indices } pub fn outputs(&self) -> &[CellMeta] { - &self.tx_data.outputs - } - - pub fn current_script_hash(&self) -> &Byte32 { - &self.script_hash + &self.tx_info.outputs } } -impl
DataSource for Arc> +impl
DataSource for SgData
where DL: CellDataProvider, { fn load_data(&self, id: &DataPieceId, offset: u64, length: u64) -> Option<(Bytes, u64)> { match id { DataPieceId::Input(i) => self - .tx_data .rtx .resolved_inputs .get(*i as usize) - .and_then(|cell| self.tx_data.data_loader.load_cell_data(cell)), + .and_then(|cell| self.data_loader().load_cell_data(cell)), DataPieceId::Output(i) => self - .tx_data .rtx .transaction .outputs_data() .get(*i as usize) .map(|data| data.raw_data()), DataPieceId::CellDep(i) => self - .tx_data .rtx .resolved_cell_deps .get(*i as usize) - .and_then(|cell| self.tx_data.data_loader.load_cell_data(cell)), + .and_then(|cell| self.data_loader().load_cell_data(cell)), DataPieceId::GroupInput(i) => self + .sg_info .script_group .input_indices .get(*i as usize) - .and_then(|gi| self.tx_data.rtx.resolved_inputs.get(*gi)) - .and_then(|cell| self.tx_data.data_loader.load_cell_data(cell)), + .and_then(|gi| self.rtx.resolved_inputs.get(*gi)) + .and_then(|cell| self.data_loader().load_cell_data(cell)), DataPieceId::GroupOutput(i) => self + .sg_info .script_group .output_indices .get(*i as usize) - .and_then(|gi| self.tx_data.rtx.transaction.outputs_data().get(*gi)) + .and_then(|gi| self.rtx.transaction.outputs_data().get(*gi)) .map(|data| data.raw_data()), DataPieceId::Witness(i) => self - .tx_data .rtx .transaction .witnesses() .get(*i as usize) .map(|data| data.raw_data()), DataPieceId::WitnessGroupInput(i) => self + .sg_info .script_group .input_indices .get(*i as usize) - .and_then(|gi| self.tx_data.rtx.transaction.witnesses().get(*gi)) + .and_then(|gi| self.rtx.transaction.witnesses().get(*gi)) .map(|data| data.raw_data()), DataPieceId::WitnessGroupOutput(i) => self + .sg_info .script_group .output_indices .get(*i as usize) - .and_then(|gi| self.tx_data.rtx.transaction.witnesses().get(*gi)) + .and_then(|gi| self.rtx.transaction.witnesses().get(*gi)) .map(|data| data.raw_data()), } .map(|data| { @@ -952,21 +1017,21 @@ where pub(crate) base_cycles: Arc, /// A mutable reference to scheduler's message box pub(crate) message_box: Arc>>, - pub(crate) snapshot2_context: Arc>>>>, + pub(crate) snapshot2_context: Arc>>>, } impl
VmContext
where - DL: CellDataProvider, + DL: CellDataProvider + Clone, { /// Creates a new VM context. It is by design that parameters to this function /// are references. It is a reminder that the inputs are designed to be shared /// among different entities. - pub fn new(sg_data: &Arc>, message_box: &Arc>>) -> Self { + pub fn new(sg_data: &SgData
, message_box: &Arc>>) -> Self { Self { base_cycles: Arc::new(AtomicU64::new(0)), message_box: Arc::clone(message_box), - snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(Arc::clone(sg_data)))), + snapshot2_context: Arc::new(Mutex::new(Snapshot2Context::new(sg_data.clone()))), } }