From 9b40aefa94052ab53090f0853723567513283caf Mon Sep 17 00:00:00 2001 From: xjd Date: Sat, 9 Mar 2024 19:15:06 +0800 Subject: [PATCH 1/2] Max VMs count (#14) * Add max_vms_count * Typo --- script/src/v2_scheduler.rs | 21 +++++++++++++----- script/src/v2_syscalls.rs | 1 + script/src/v2_types.rs | 1 + .../tests/ckb_latest/features_since_v2023.rs | 8 ++++++- script/testdata/spawn_cases | Bin 12048 -> 12384 bytes script/testdata/spawn_cases.c | 19 ++++++++++++++++ 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/script/src/v2_scheduler.rs b/script/src/v2_scheduler.rs index ae899959c41..2638c8ac6b9 100644 --- a/script/src/v2_scheduler.rs +++ b/script/src/v2_scheduler.rs @@ -1,7 +1,7 @@ -use crate::types::CoreMachine as ICoreMachine; -use crate::v2_syscalls::INDEX_OUT_OF_BOUND; +use crate::v2_syscalls::{INDEX_OUT_OF_BOUND, MAX_VMS_SPAWNED}; use crate::v2_types::PipeIoArgs; use crate::verify::TransactionScriptsSyscallsGenerator; +use crate::ScriptVersion; use crate::{ v2_syscalls::{ transferred_byte_cycles, MachineContext, INVALID_PIPE, OTHER_END_CLOSED, SUCCESS, @@ -12,7 +12,6 @@ use crate::{ FIRST_PIPE_SLOT, FIRST_VM_ID, }, }; -use crate::{ScriptVersion, TransactionScriptsVerifier}; use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_types::core::Cycle; use ckb_vm::snapshot2::Snapshot2Context; @@ -27,7 +26,7 @@ use ckb_vm::{ memory::Memory, registers::A0, snapshot2::{DataSource, Snapshot2}, - Error, Register, Syscalls, + Error, Register, }; use std::sync::{Arc, Mutex}; use std::{ @@ -36,6 +35,7 @@ use std::{ }; const ROOT_VM_ID: VmId = FIRST_VM_ID; +const MAX_VMS_COUNT: u64 = 16; const MAX_INSTANTIATED_VMS: usize = 4; /// A single Scheduler instance is used to verify a single script @@ -56,6 +56,7 @@ where script_version: ScriptVersion, syscalls_generator: TransactionScriptsSyscallsGenerator
, + max_vms_count: u64, total_cycles: Cycle, next_vm_id: VmId, next_pipe_slot: u64, @@ -86,6 +87,7 @@ where tx_data, script_version, syscalls_generator, + max_vms_count: MAX_VMS_COUNT, total_cycles: 0, next_vm_id: FIRST_VM_ID, next_pipe_slot: FIRST_PIPE_SLOT, @@ -115,6 +117,7 @@ where tx_data, script_version, syscalls_generator, + max_vms_count: full.max_vms_count, total_cycles: full.total_cycles, next_vm_id: full.next_vm_id, next_pipe_slot: full.next_pipe_slot, @@ -138,6 +141,7 @@ where /// Suspend current scheduler into a serializable full state pub fn suspend(mut self) -> Result { + assert!(self.message_box.lock().expect("lock").is_empty()); let mut vms = Vec::with_capacity(self.states.len()); let instantiated_ids: Vec<_> = self.instantiated.keys().cloned().collect(); for id in instantiated_ids { @@ -148,6 +152,7 @@ where vms.push((id, state, snapshot)); } Ok(FullSuspendedState { + max_vms_count: self.max_vms_count, total_cycles: self.total_cycles, next_vm_id: self.next_vm_id, next_pipe_slot: self.next_pipe_slot, @@ -316,7 +321,13 @@ where if !pipes_valid { continue; } - // TODO: spawn limits + if self.suspended.len() + self.instantiated.len() > self.max_vms_count as usize + { + self.ensure_vms_instantiated(&[vm_id])?; + let (_, machine) = self.instantiated.get_mut(&vm_id).unwrap(); + machine.machine.set_register(A0, MAX_VMS_SPAWNED as u64); + continue; + } let spawned_vm_id = self.boot_vm(&args.data_piece_id, args.offset, args.length, &args.argv)?; // Move passed pipes from spawner to spawnee diff --git a/script/src/v2_syscalls.rs b/script/src/v2_syscalls.rs index 087ac72e507..cafbe516bd8 100644 --- a/script/src/v2_syscalls.rs +++ b/script/src/v2_syscalls.rs @@ -239,3 +239,4 @@ pub(crate) const SLICE_OUT_OF_BOUND: u8 = 3; pub(crate) const WAIT_FAILURE: u8 = 5; pub(crate) const INVALID_PIPE: u8 = 6; pub(crate) const OTHER_END_CLOSED: u8 = 7; +pub(crate) const MAX_VMS_SPAWNED: u8 = 8; diff --git a/script/src/v2_types.rs b/script/src/v2_types.rs index 599dbe6abb1..42e5eb6a192 100644 --- a/script/src/v2_types.rs +++ b/script/src/v2_types.rs @@ -141,6 +141,7 @@ impl TryFrom<(u64, u64, u64)> for DataPieceId { /// fully recover the running environment with the full transaction environment. #[derive(Clone, Debug)] pub struct FullSuspendedState { + pub max_vms_count: u64, pub total_cycles: Cycle, pub next_vm_id: VmId, pub next_pipe_slot: u64, 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 73c4f263a89..77df460c086 100644 --- a/script/src/verify/tests/ckb_latest/features_since_v2023.rs +++ b/script/src/verify/tests/ckb_latest/features_since_v2023.rs @@ -95,6 +95,12 @@ fn check_spawn_read_then_close() { assert_eq!(result.is_ok(), SCRIPT_VERSION == ScriptVersion::V2); } +#[test] +fn check_spawn_max_vms_count() { + let result = simple_spawn_test("testdata/spawn_cases", &[10]); + assert_eq!(result.is_ok(), SCRIPT_VERSION == ScriptVersion::V2); +} + #[test] fn check_vm_version() { let script_version = SCRIPT_VERSION; @@ -348,7 +354,7 @@ fn check_spawn_recursive() { let result = verifier.verify(script_version, &rtx, 70_000_000); if script_version >= ScriptVersion::V2 { let msg = result.unwrap_err().to_string(); - assert!(msg.contains("ExceededMaximumCycles")) + assert!(msg.contains("error code 8")) } else { assert!(result.is_err()) } diff --git a/script/testdata/spawn_cases b/script/testdata/spawn_cases index 06ee2e2349c72bde12dff96ab309fd7e69657e99..b80d3cd078d2145c4c1bb94149d3cc6071742c94 100755 GIT binary patch delta 3194 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 Aw*UYD diff --git a/script/testdata/spawn_cases.c b/script/testdata/spawn_cases.c index 712cace0d0c..f43a4505b27 100644 --- a/script/testdata/spawn_cases.c +++ b/script/testdata/spawn_cases.c @@ -320,6 +320,20 @@ int child_read_then_close() { return err; } +int parent_max_vms_count() { + const char* argv[2] = {"", 0}; + return simple_spawn_args(0, 1, argv); +} + +int child_max_vms_count() { + const char* argv[2] = {"", 0}; + int err = simple_spawn_args(0, 1, argv); + CHECK2(err == 0 || err == CKB_MAX_VMS_SPAWNED, -2); + err = 0; +exit: + return err; +} + int parent_entry(int case_id) { int err = 0; uint64_t pid = 0; @@ -341,6 +355,9 @@ int parent_entry(int case_id) { err = parent_inherited_fds_without_owner(&pid); } else if (case_id == 9) { err = parent_read_then_close(&pid); + } else if (case_id == 10) { + err = parent_max_vms_count(&pid); + return err; } else { CHECK2(false, -2); } @@ -373,6 +390,8 @@ int child_entry(int case_id) { return 0; } else if (case_id == 9) { return child_read_then_close(); + } else if (case_id == 10) { + return child_max_vms_count(); } else { return -1; } From 0481dec1197013aa1d5432991b4d42de81aa55a4 Mon Sep 17 00:00:00 2001 From: Mohanson Date: Sat, 9 Mar 2024 19:16:12 +0800 Subject: [PATCH 2/2] Rewrite load_cell_data (#15) --- script/src/syscalls/load_cell_data.rs | 201 ++++++++---------- .../syscalls/tests/vm_latest/syscalls_1.rs | 195 +++++++++++++---- script/src/v2_syscalls.rs | 123 ----------- script/src/verify.rs | 19 +- 4 files changed, 242 insertions(+), 296 deletions(-) diff --git a/script/src/syscalls/load_cell_data.rs b/script/src/syscalls/load_cell_data.rs index 11adfadca75..5147e44f8d3 100644 --- a/script/src/syscalls/load_cell_data.rs +++ b/script/src/syscalls/load_cell_data.rs @@ -1,86 +1,73 @@ -use crate::types::Indices; +use crate::v2_types::{DataPieceId, TxData}; use crate::{ cost_model::transferred_byte_cycles, syscalls::{ - utils::store_data, Source, SourceEntry, INDEX_OUT_OF_BOUND, - LOAD_CELL_DATA_AS_CODE_SYSCALL_NUMBER, LOAD_CELL_DATA_SYSCALL_NUMBER, SLICE_OUT_OF_BOUND, - SUCCESS, + INDEX_OUT_OF_BOUND, LOAD_CELL_DATA_AS_CODE_SYSCALL_NUMBER, LOAD_CELL_DATA_SYSCALL_NUMBER, + SLICE_OUT_OF_BOUND, SUCCESS, }, }; -use ckb_traits::CellDataProvider; -use ckb_types::core::cell::{CellMeta, ResolvedTransaction}; +use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; use ckb_vm::{ memory::{Memory, FLAG_EXECUTABLE, FLAG_FREEZED}, registers::{A0, A1, A2, A3, A4, A5, A7}, + snapshot2::{DataSource, Snapshot2Context}, Error as VMError, Register, SupportMachine, Syscalls, }; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; -pub struct LoadCellData
{ - data_loader: DL, - rtx: Arc, - outputs: Arc>, - group_inputs: Indices, - group_outputs: Indices, +pub struct LoadCellData
+where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, +{ + snapshot2_context: Arc>>>, } -impl LoadCellData
{ +impl
LoadCellData
+where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, +{ pub fn new( - data_loader: DL, - rtx: Arc, - outputs: Arc>, - group_inputs: Indices, - group_outputs: Indices, + snapshot2_context: Arc>>>, ) -> LoadCellData
{ - LoadCellData { - data_loader, - rtx, - outputs, - group_inputs, - group_outputs, - } - } - - #[inline] - fn resolved_inputs(&self) -> &Vec { - &self.rtx.resolved_inputs - } - - #[inline] - fn resolved_cell_deps(&self) -> &Vec { - &self.rtx.resolved_cell_deps + LoadCellData { snapshot2_context } } - fn fetch_cell(&self, source: Source, index: usize) -> Result<&CellMeta, u8> { - match source { - Source::Transaction(SourceEntry::Input) => { - 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) + fn load_data(&self, machine: &mut Mac) -> Result<(), VMError> { + let index = machine.registers()[A3].to_u64(); + let source = machine.registers()[A4].to_u64(); + let data_piece_id = match DataPieceId::try_from((source, index, 0)) { + Ok(id) => id, + Err(_) => { + machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); + return Ok(()); } - Source::Transaction(SourceEntry::CellDep) => self - .resolved_cell_deps() - .get(index) - .ok_or(INDEX_OUT_OF_BOUND), - Source::Transaction(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), - Source::Group(SourceEntry::Input) => self - .group_inputs - .get(index) - .ok_or(INDEX_OUT_OF_BOUND) - .and_then(|actual_index| { - self.resolved_inputs() - .get(*actual_index) - .ok_or(INDEX_OUT_OF_BOUND) - }), - Source::Group(SourceEntry::Output) => self - .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)), - Source::Group(SourceEntry::CellDep) => Err(INDEX_OUT_OF_BOUND), - Source::Group(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), - } + }; + let addr = machine.registers()[A0].to_u64(); + let size_addr = machine.registers()[A1].clone(); + let size = machine.memory_mut().load64(&size_addr)?.to_u64(); + let offset = machine.registers()[A2].to_u64(); + let mut sc = self + .snapshot2_context + .lock() + .map_err(|e| VMError::Unexpected(e.to_string()))?; + + let (wrote_size, full_size) = + match sc.store_bytes(machine, addr, &data_piece_id, offset, size) { + Ok(val) => val, + Err(VMError::External(m)) if m == "INDEX_OUT_OF_BOUND" => { + // This comes from TxData results in an out of bound error, to + // mimic current behavior, we would return INDEX_OUT_OF_BOUND error. + machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); + return Ok(()); + } + Err(e) => return Err(e), + }; + machine + .memory_mut() + .store64(&size_addr, &Mac::REG::from_u64(full_size))?; + machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; + machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); + Ok(()) } fn load_data_as_code(&self, machine: &mut Mac) -> Result<(), VMError> { @@ -88,75 +75,63 @@ impl LoadCellData
{ let memory_size = machine.registers()[A1].to_u64(); let content_offset = machine.registers()[A2].to_u64(); let content_size = machine.registers()[A3].to_u64(); - let index = machine.registers()[A4].to_u64(); - let source = Source::parse_from_u64(machine.registers()[A5].to_u64())?; - - let cell = self.fetch_cell(source, index as usize); - if let Err(err) = cell { - machine.set_register(A0, Mac::REG::from_u8(err)); - return Ok(()); - } - let cell = cell.unwrap(); - + let source = machine.registers()[A5].to_u64(); + let data_piece_id = match DataPieceId::try_from((source, index, 0)) { + Ok(id) => id, + Err(_) => { + machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); + return Ok(()); + } + }; + let mut sc = self + .snapshot2_context + .lock() + .map_err(|e| VMError::Unexpected(e.to_string()))?; + // We are using 0..u64::max_value() to fetch full cell, there is + // also no need to keep the full length value. Since cell's length + // is already full length. + let (cell, _) = match sc + .data_source() + .load_data(&data_piece_id, 0, u64::max_value()) + { + Ok(val) => val, + Err(VMError::External(m)) if m == "INDEX_OUT_OF_BOUND" => { + // This comes from TxData results in an out of bound error, to + // mimic current behavior, we would return INDEX_OUT_OF_BOUND error. + machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); + return Ok(()); + } + Err(e) => return Err(e), + }; let content_end = content_offset .checked_add(content_size) .ok_or(VMError::MemOutOfBound)?; - if content_offset >= cell.data_bytes - || content_end > cell.data_bytes + if content_offset >= cell.len() as u64 + || content_end > cell.len() as u64 || content_size > memory_size { machine.set_register(A0, Mac::REG::from_u8(SLICE_OUT_OF_BOUND)); return Ok(()); } - let data = self - .data_loader - .load_cell_data(cell) - .ok_or_else(|| { - VMError::Unexpected(format!( - "Unexpected load_cell_data failed {}", - cell.out_point, - )) - })? - .slice((content_offset as usize)..(content_end as usize)); machine.memory_mut().init_pages( addr, memory_size, FLAG_EXECUTABLE | FLAG_FREEZED, - Some(data), + Some(cell.slice((content_offset as usize)..(content_end as usize))), 0, )?; - + sc.track_pages(machine, addr, memory_size, &data_piece_id, content_offset)?; machine.add_cycles_no_checking(transferred_byte_cycles(memory_size))?; machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); Ok(()) } - - fn load_data(&self, machine: &mut Mac) -> Result<(), VMError> { - let index = machine.registers()[A3].to_u64(); - let source = Source::parse_from_u64(machine.registers()[A4].to_u64())?; - - let cell = self.fetch_cell(source, index as usize); - if let Err(err) = cell { - machine.set_register(A0, Mac::REG::from_u8(err)); - return Ok(()); - } - let cell = cell.unwrap(); - let data = self.data_loader.load_cell_data(cell).ok_or_else(|| { - VMError::Unexpected(format!( - "Unexpected load_cell_data failed {}", - cell.out_point, - )) - })?; - - let wrote_size = store_data(machine, &data)?; - machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; - machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); - Ok(()) - } } -impl Syscalls for LoadCellData
{ +impl Syscalls for LoadCellData
+where + DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static, +{ fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { Ok(()) } diff --git a/script/src/syscalls/tests/vm_latest/syscalls_1.rs b/script/src/syscalls/tests/vm_latest/syscalls_1.rs index 391320a5565..5e67fd53462 100644 --- a/script/src/syscalls/tests/vm_latest/syscalls_1.rs +++ b/script/src/syscalls/tests/vm_latest/syscalls_1.rs @@ -14,14 +14,17 @@ use ckb_types::{ use ckb_vm::{ memory::{FLAG_DIRTY, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE}, registers::{A0, A1, A2, A3, A4, A5, A7}, + snapshot2::{DataSource, Snapshot2, Snapshot2Context}, CoreMachine, Error as VMError, Memory, Syscalls, RISCV_PAGESIZE, }; use proptest::{collection::size_range, prelude::*}; use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use super::SCRIPT_VERSION; use crate::syscalls::{tests::utils::*, *}; +use crate::types::{ScriptGroup, ScriptGroupType}; +use crate::v2_types::TxData; fn _test_load_cell_not_exist(data: &[u8]) -> Result<(), TestCaseError> { let mut machine = SCRIPT_VERSION.init_core_machine_without_limit(); @@ -1453,17 +1456,26 @@ fn _test_load_cell_data_as_code( let output = build_cell_meta(100, data.clone()); let input_cell = build_cell_meta(100, data.clone()); - 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(), + transaction: TransactionBuilder::default() + .output_data(data.pack()) + .build(), resolved_cell_deps: vec![dep_cell], resolved_inputs: vec![input_cell], resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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], + }), + })))); prop_assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1474,7 +1486,7 @@ fn _test_load_cell_data_as_code( } else { prop_assert_eq!(machine.registers()[A0], u64::from(SUCCESS)); - let flags = FLAG_EXECUTABLE | FLAG_FREEZED | FLAG_DIRTY; + let flags = FLAG_EXECUTABLE | FLAG_FREEZED; prop_assert_eq!( machine .memory_mut() @@ -1517,19 +1529,27 @@ fn _test_load_cell_data( let dep_cell = build_cell_meta(10000, data.clone()); let output = build_cell_meta(100, data.clone()); let input_cell = build_cell_meta(100, data.clone()); - - 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(), + transaction: TransactionBuilder::default() + .output_data(data.pack()) + .build(), resolved_cell_deps: vec![dep_cell], resolved_inputs: vec![input_cell], resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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], + }), + })))); prop_assert!(load_code.ecall(&mut machine).is_ok()); @@ -1537,8 +1557,11 @@ fn _test_load_cell_data( prop_assert_eq!(machine.registers()[A0], u64::from(code)); } else { prop_assert_eq!(machine.registers()[A0], u64::from(SUCCESS)); - - let flags = FLAG_WRITABLE | FLAG_DIRTY; + let flags = if data.len() < RISCV_PAGESIZE { + FLAG_WRITABLE | FLAG_DIRTY + } else { + FLAG_WRITABLE + }; prop_assert_eq!( machine .memory_mut() @@ -1618,9 +1641,6 @@ fn test_load_overflowed_cell_data_as_code() { let dep_cell = build_cell_meta(10000, dep_cell_data); let data_loader = new_mock_data_loader(); - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -1629,7 +1649,17 @@ fn test_load_overflowed_cell_data_as_code() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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(), + }), + })))); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1637,7 +1667,61 @@ fn test_load_overflowed_cell_data_as_code() { assert_eq!(result.unwrap_err(), VMError::MemOutOfBound); } -fn _test_load_cell_data_on_freezed_memory(as_code: bool, data: &[u8]) -> Result<(), TestCaseError> { +fn _test_load_cell_data_on_freezed_memory(data: &[u8]) -> Result<(), TestCaseError> { + let mut machine = SCRIPT_VERSION.init_core_machine_without_limit(); + let addr = 8192; + let addr_size = 4096; + let size_addr = 100; + + prop_assert!(machine + .memory_mut() + .store64(&size_addr, &(data.len() as u64)) + .is_ok()); + prop_assert!(machine + .memory_mut() + .init_pages(addr, addr_size, FLAG_EXECUTABLE | FLAG_FREEZED, None, 0) + .is_ok()); + + machine.set_register(A0, addr); // addr + machine.set_register(A1, size_addr); // size + machine.set_register(A2, 0); // content offset + machine.set_register(A3, 0); //index + machine.set_register(A4, u64::from(Source::Transaction(SourceEntry::CellDep))); //source + machine.set_register(A7, LOAD_CELL_DATA_SYSCALL_NUMBER); // syscall number + + 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], + resolved_inputs: vec![], + resolved_dep_groups: vec![], + }); + + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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(), + }), + })))); + + prop_assert!(load_code.ecall(&mut machine).is_err()); + + for i in addr..addr + addr_size { + assert_eq!(machine.memory_mut().load8(&i), Ok(0)); + } + Ok(()) +} + +fn _test_load_cell_data_as_code_on_freezed_memory(data: &[u8]) -> Result<(), TestCaseError> { let mut machine = SCRIPT_VERSION.init_core_machine_without_limit(); let addr = 8192; let addr_size = 4096; @@ -1653,20 +1737,12 @@ fn _test_load_cell_data_on_freezed_memory(as_code: bool, data: &[u8]) -> Result< machine.set_register(A3, data.len() as u64); // content size machine.set_register(A4, 0); //index machine.set_register(A5, u64::from(Source::Transaction(SourceEntry::CellDep))); //source - let syscall = if as_code { - LOAD_CELL_DATA_AS_CODE_SYSCALL_NUMBER - } else { - LOAD_CELL_DATA_SYSCALL_NUMBER - }; - machine.set_register(A7, syscall); // syscall number + machine.set_register(A7, LOAD_CELL_DATA_AS_CODE_SYSCALL_NUMBER); // syscall number 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 outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -1675,7 +1751,17 @@ fn _test_load_cell_data_on_freezed_memory(as_code: bool, data: &[u8]) -> Result< resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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(), + }), + })))); prop_assert!(load_code.ecall(&mut machine).is_err()); @@ -1691,12 +1777,12 @@ proptest! { })] #[test] fn test_load_code_on_freezed_memory(ref data in any_with::>(size_range(4096).lift())) { - _test_load_cell_data_on_freezed_memory(true, data)?; + _test_load_cell_data_as_code_on_freezed_memory(data)?; } #[test] fn test_load_data_on_freezed_memory(ref data in any_with::>(size_range(4096).lift())) { - _test_load_cell_data_on_freezed_memory(false, data)?; + _test_load_cell_data_on_freezed_memory(data)?; } } @@ -1718,9 +1804,6 @@ fn test_load_code_unaligned_error() { let dep_cell = build_cell_meta(10000, dep_cell_data); let data_loader = new_mock_data_loader(); - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -1729,7 +1812,17 @@ fn test_load_code_unaligned_error() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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(), + }), + })))); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1759,9 +1852,6 @@ fn test_load_code_slice_out_of_bound_error() { let dep_cell = build_cell_meta(10000, dep_cell_data); let data_loader = new_mock_data_loader(); - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -1770,7 +1860,17 @@ fn test_load_code_slice_out_of_bound_error() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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(), + }), + })))); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); @@ -1803,9 +1903,6 @@ fn test_load_code_not_enough_space_error() { let dep_cell = build_cell_meta(10000, dep_cell_data); let data_loader = new_mock_data_loader(); - let outputs = Arc::new(vec![]); - let group_inputs = Arc::new(vec![]); - let group_outputs = Arc::new(vec![]); let rtx = Arc::new(ResolvedTransaction { transaction: TransactionBuilder::default().build(), @@ -1814,7 +1911,17 @@ fn test_load_code_not_enough_space_error() { resolved_dep_groups: vec![], }); - let mut load_code = LoadCellData::new(data_loader, rtx, outputs, group_inputs, group_outputs); + let mut load_code = LoadCellData::new(Arc::new(Mutex::new(Snapshot2Context::new(TxData { + rtx: rtx, + data_loader: 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(), + }), + })))); assert!(machine.memory_mut().store_byte(addr, addr_size, 1).is_ok()); diff --git a/script/src/v2_syscalls.rs b/script/src/v2_syscalls.rs index cafbe516bd8..74efd69b9c0 100644 --- a/script/src/v2_syscalls.rs +++ b/script/src/v2_syscalls.rs @@ -53,127 +53,6 @@ where *self.base_cycles.lock().expect("lock") = base_cycles; } - // Reimplementation of load_cell_data but keep tracks of pages that are copied from - // surrounding transaction data. Those pages do not need to be added to snapshots. - fn load_cell_data(&mut self, machine: &mut Mac) -> Result<(), Error> { - let index = machine.registers()[A3].to_u64(); - let source = machine.registers()[A4].to_u64(); - - let data_piece_id = match DataPieceId::try_from((source, index, 0)) { - Ok(id) => id, - Err(e) => { - // Current implementation would throw an error immediately - // for some source values, but return INDEX_OUT_OF_BOUND error - // for other values. Here for simplicity, we would return - // INDEX_OUT_OF_BOUND error in all cases. But the code might - // differ to mimic current on-chain behavior - println!("DataPieceId parsing error: {:?}", e); - machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); - return Ok(()); - } - }; - - let addr = machine.registers()[A0].to_u64(); - let size_addr = machine.registers()[A1].clone(); - let size = machine.memory_mut().load64(&size_addr)?.to_u64(); - let offset = machine.registers()[A2].to_u64(); - - let mut sc = self.snapshot2_context().lock().expect("lock"); - let (wrote_size, full_size) = - match sc.store_bytes(machine, addr, &data_piece_id, offset, size) { - Ok(val) => val, - Err(Error::External(m)) if m == "INDEX_OUT_OF_BOUND" => { - // This comes from TxData results in an out of bound error, to - // mimic current behavior, we would return INDEX_OUT_OF_BOUND error. - machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); - return Ok(()); - } - Err(e) => return Err(e), - }; - - machine - .memory_mut() - .store64(&size_addr, &Mac::REG::from_u64(full_size))?; - machine.add_cycles_no_checking(transferred_byte_cycles(wrote_size))?; - machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); - Ok(()) - } - - // Reimplementation of load_cell_data_as_code but keep tracks of pages that are copied from - // surrounding transaction data. Those pages do not need to be added to snapshots. - // - // Different from load_cell_data, this method showcases advanced usage of Snapshot2, where - // one manually does the actual memory copying, then calls track_pages method to setup metadata - // used by Snapshot2. It does not rely on higher level methods provided by Snapshot2. - fn load_cell_data_as_code( - &mut self, - machine: &mut Mac, - ) -> Result<(), Error> { - let addr = machine.registers()[A0].to_u64(); - let memory_size = machine.registers()[A1].to_u64(); - let content_offset = machine.registers()[A2].to_u64(); - let content_size = machine.registers()[A3].to_u64(); - - let index = machine.registers()[A4].to_u64(); - let source = machine.registers()[A5].to_u64(); - - let data_piece_id = match DataPieceId::try_from((source, index, 0)) { - Ok(id) => id, - Err(e) => { - // Current implementation would throw an error immediately - // for some source values, but return INDEX_OUT_OF_BOUND error - // for other values. Here for simplicity, we would return - // INDEX_OUT_OF_BOUND error in all cases. But the code might - // differ to mimic current on-chain behavior - println!("DataPieceId parsing error: {:?}", e); - machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); - return Ok(()); - } - }; - - let mut sc = self.snapshot2_context().lock().expect("lock"); - // We are using 0..u64::max_value() to fetch full cell, there is - // also no need to keep the full length value. Since cell's length - // is already full length. - let (cell, _) = match sc - .data_source() - .load_data(&data_piece_id, 0, u64::max_value()) - { - Ok(val) => val, - Err(Error::External(m)) if m == "INDEX_OUT_OF_BOUND" => { - // This comes from TxData results in an out of bound error, to - // mimic current behavior, we would return INDEX_OUT_OF_BOUND error. - machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); - return Ok(()); - } - Err(e) => return Err(e), - }; - - let content_end = content_offset - .checked_add(content_size) - .ok_or(Error::MemOutOfBound)?; - if content_offset >= cell.len() as u64 - || content_end > cell.len() as u64 - || content_size > memory_size - { - machine.set_register(A0, Mac::REG::from_u8(SLICE_OUT_OF_BOUND)); - return Ok(()); - } - - machine.memory_mut().init_pages( - addr, - memory_size, - FLAG_EXECUTABLE | FLAG_FREEZED, - Some(cell.slice((content_offset as usize)..(content_end as usize))), - 0, - )?; - sc.track_pages(machine, addr, memory_size, &data_piece_id, content_offset)?; - - machine.add_cycles_no_checking(transferred_byte_cycles(memory_size))?; - machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); - Ok(()) - } - // Reimplementing debug syscall for printing debug messages fn debug(&mut self, machine: &mut Mac) -> Result<(), Error> { let mut addr = machine.registers()[A0].to_u64(); @@ -211,8 +90,6 @@ where fn ecall(&mut self, machine: &mut Mac) -> Result { let code = machine.registers()[A7].to_u64(); match code { - 2091 => self.load_cell_data_as_code(machine), - 2092 => self.load_cell_data(machine), 2177 => self.debug(machine), _ => return Ok(false), }?; diff --git a/script/src/verify.rs b/script/src/verify.rs index 5f014cd6174..7341316fa55 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -202,18 +202,8 @@ where } /// Build syscall: load_cell_data - pub fn build_load_cell_data( - &self, - group_inputs: Indices, - group_outputs: Indices, - ) -> LoadCellData
{ - LoadCellData::new( - self.data_loader.clone(), - Arc::clone(&self.rtx), - Arc::clone(&self.outputs), - group_inputs, - group_outputs, - ) + pub fn build_load_cell_data(&self) -> LoadCellData
{ + LoadCellData::new(self.snapshot2_context.clone()) } ///Build syscall: load_input @@ -321,10 +311,7 @@ where 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(&script_group_input_indices), - Arc::clone(&script_group_output_indices), - )), + Box::new(self.build_load_cell_data()), Box::new(Debugger::new( current_script_hash, Arc::clone(&self.debug_printer),