diff --git a/script/Cargo.toml b/script/Cargo.toml index 0acc8c15eb..ae31635bef 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] numext-fixed-hash = { version = "0.1", features = ["support_rand", "support_heapsize", "support_serde"] } +byteorder = "1.2.2" crypto = {path = "../util/crypto"} ckb-core = { path = "../core" } serde = "1.0" diff --git a/script/src/syscalls/load_cell.rs b/script/src/syscalls/load_cell.rs new file mode 100644 index 0000000000..c8fed1d814 --- /dev/null +++ b/script/src/syscalls/load_cell.rs @@ -0,0 +1,81 @@ +use crate::syscalls::{Source, LOAD_CELL_SYSCALL_NUMBER, SUCCESS}; +use ckb_core::transaction::CellOutput; +use ckb_protocol::CellOutput as FbsCellOutput; +use ckb_vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A4, A7}; +use flatbuffers::FlatBufferBuilder; +use std::cmp; + +#[derive(Debug)] +pub struct LoadCell<'a> { + outputs: &'a [&'a CellOutput], + input_cells: &'a [&'a CellOutput], + current: &'a CellOutput, +} + +impl<'a> LoadCell<'a> { + pub fn new( + outputs: &'a [&'a CellOutput], + input_cells: &'a [&'a CellOutput], + current: &'a CellOutput, + ) -> LoadCell<'a> { + LoadCell { + outputs, + input_cells, + current, + } + } + + fn fetch_cell(&self, source: Source, index: usize) -> Option<&CellOutput> { + match source { + Source::Input => self.input_cells.get(index).cloned(), + Source::Output => self.outputs.get(index).cloned(), + Source::Current => Some(self.current), + } + } +} + +impl<'a, R: Register, M: Memory> Syscalls for LoadCell<'a> { + fn initialize(&mut self, _machine: &mut CoreMachine) -> Result<(), VMError> { + Ok(()) + } + + fn ecall(&mut self, machine: &mut CoreMachine) -> Result { + if machine.registers()[A7].to_u64() != LOAD_CELL_SYSCALL_NUMBER { + return Ok(false); + } + + let addr = machine.registers()[A0].to_usize(); + let size_addr = machine.registers()[A1].to_usize(); + let size = machine.memory_mut().load64(size_addr)? as usize; + + let index = machine.registers()[A3].to_usize(); + let source = Source::parse_from_u64(machine.registers()[A4].to_u64())?; + + let cell = self + .fetch_cell(source, index) + .ok_or_else(|| VMError::OutOfBound)?; + + // NOTE: this is a very expensive operation here since we need to copy + // everything in a cell to a flatbuffer object, serialize the object + // into a buffer, and then copy requested data to VM memory space. So + // we should charge cycles proportional to the full Cell size no matter + // how much data the actual script is requesting, the per-byte cycle charged + // here, should also be significantly higher than LOAD_CELL_BY_FIELD. + // Also, while this is debatable, I suggest we charge full cycles for + // subsequent calls even if we have cache implemented here. + // TODO: find a way to cache this without consuming too much memory + let mut builder = FlatBufferBuilder::new(); + let offset = FbsCellOutput::build(&mut builder, cell); + builder.finish(offset, None); + let data = builder.finished_data(); + + let offset = machine.registers()[A2].to_usize(); + let real_size = cmp::min(size, data.len() - offset); + machine.memory_mut().store64(size_addr, real_size as u64)?; + machine + .memory_mut() + .store_bytes(addr, &data[offset..offset + real_size])?; + machine.registers_mut()[A0] = R::from_u8(SUCCESS); + Ok(true) + } +} diff --git a/script/src/syscalls/load_cell_by_field.rs b/script/src/syscalls/load_cell_by_field.rs new file mode 100644 index 0000000000..d5b2d98ef3 --- /dev/null +++ b/script/src/syscalls/load_cell_by_field.rs @@ -0,0 +1,112 @@ +use byteorder::{LittleEndian, WriteBytesExt}; +use ckb_core::transaction::CellOutput; +use ckb_protocol::Script as FbsScript; +use crate::syscalls::{Field, Source, ITEM_MISSING, LOAD_CELL_BY_FIELD_SYSCALL_NUMBER, SUCCESS}; +use ckb_vm::{ + CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A4, A5, A7, +}; +use flatbuffers::FlatBufferBuilder; +use std::cmp; + +#[derive(Debug)] +pub struct LoadCellByField<'a> { + outputs: &'a [&'a CellOutput], + input_cells: &'a [&'a CellOutput], + current: &'a CellOutput, +} + +impl<'a> LoadCellByField<'a> { + pub fn new( + outputs: &'a [&'a CellOutput], + input_cells: &'a [&'a CellOutput], + current: &'a CellOutput, + ) -> LoadCellByField<'a> { + LoadCellByField { + outputs, + input_cells, + current, + } + } + + fn fetch_cell(&self, source: Source, index: usize) -> Option<&CellOutput> { + match source { + Source::Input => self.input_cells.get(index).cloned(), + Source::Output => self.outputs.get(index).cloned(), + Source::Current => Some(self.current), + } + } +} + +fn store_data( + machine: &mut CoreMachine, + data: &[u8], +) -> Result<(), VMError> { + let addr = machine.registers()[A0].to_usize(); + let size_addr = machine.registers()[A1].to_usize(); + let offset = machine.registers()[A2].to_usize(); + + let size = machine.memory_mut().load64(size_addr)? as usize; + let real_size = cmp::min(size, data.len() - offset); + machine.memory_mut().store64(size_addr, real_size as u64)?; + machine + .memory_mut() + .store_bytes(addr, &data[offset..offset + real_size])?; + Ok(()) +} + +impl<'a, R: Register, M: Memory> Syscalls for LoadCellByField<'a> { + fn initialize(&mut self, _machine: &mut CoreMachine) -> Result<(), VMError> { + Ok(()) + } + + fn ecall(&mut self, machine: &mut CoreMachine) -> Result { + if machine.registers()[A7].to_u64() != LOAD_CELL_BY_FIELD_SYSCALL_NUMBER { + return Ok(false); + } + + let index = machine.registers()[A3].to_usize(); + let source = Source::parse_from_u64(machine.registers()[A4].to_u64())?; + let field = Field::parse_from_u64(machine.registers()[A5].to_u64())?; + + let cell = self + .fetch_cell(source, index) + .ok_or_else(|| VMError::OutOfBound)?; + + let return_code = match field { + Field::Capacity => { + let mut buffer = vec![]; + buffer.write_u64::(cell.capacity)?; + store_data(machine, &buffer)?; + SUCCESS + } + Field::Data => { + store_data(machine, &cell.data)?; + SUCCESS + } + Field::LockHash => { + store_data(machine, &cell.lock.as_bytes())?; + SUCCESS + } + Field::Contract => match cell.contract { + Some(ref contract) => { + let mut builder = FlatBufferBuilder::new(); + let offset = FbsScript::build(&mut builder, &contract); + builder.finish(offset, None); + let data = builder.finished_data(); + store_data(machine, data)?; + SUCCESS + } + None => ITEM_MISSING, + }, + Field::ContractHash => match cell.contract { + Some(ref contract) => { + store_data(machine, &contract.type_hash().as_bytes())?; + SUCCESS + } + None => ITEM_MISSING, + }, + }; + machine.registers_mut()[A0] = R::from_u8(return_code); + Ok(true) + } +} diff --git a/script/src/syscalls/load_tx.rs b/script/src/syscalls/load_tx.rs new file mode 100644 index 0000000000..295ba43859 --- /dev/null +++ b/script/src/syscalls/load_tx.rs @@ -0,0 +1,40 @@ +use ckb_vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A7}; +use std::cmp; +use syscalls::{LOAD_TX_SYSCALL_NUMBER, SUCCESS}; + +pub struct LoadTx<'a> { + tx: &'a [u8], +} + +impl<'a> LoadTx<'a> { + pub fn new(tx: &'a [u8]) -> LoadTx<'a> { + LoadTx { tx } + } +} + +impl<'a, R: Register, M: Memory> Syscalls for LoadTx<'a> { + fn initialize(&mut self, _machine: &mut CoreMachine) -> Result<(), VMError> { + Ok(()) + } + + fn ecall(&mut self, machine: &mut CoreMachine) -> Result { + if machine.registers()[A7].to_u64() != LOAD_TX_SYSCALL_NUMBER { + return Ok(false); + } + + let addr = machine.registers()[A0].to_usize(); + let size_addr = machine.registers()[A1].to_usize(); + let size = machine.memory_mut().load64(size_addr)? as usize; + + let data = self.tx; + + let offset = machine.registers()[A2].to_usize(); + let real_size = cmp::min(size, data.len() - offset); + machine.memory_mut().store64(size_addr, real_size as u64)?; + machine + .memory_mut() + .store_bytes(addr, &data[offset..offset + real_size])?; + machine.registers_mut()[A0] = R::from_u8(SUCCESS); + Ok(true) + } +} diff --git a/script/src/syscalls/mmap_cell.rs b/script/src/syscalls/mmap_cell.rs deleted file mode 100644 index ad50be23cd..0000000000 --- a/script/src/syscalls/mmap_cell.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::syscalls::{Mode, Source, MMAP_CELL_SYSCALL_NUMBER, OVERRIDE_LEN, SUCCESS}; -use ckb_core::transaction::CellOutput; -use ckb_vm::memory::PROT_READ; -use ckb_vm::{ - CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A4, A5, A7, -}; -use std::cmp; -use std::rc::Rc; - -#[derive(Debug)] -pub struct MmapCell<'a> { - outputs: &'a [&'a CellOutput], - input_cells: &'a [&'a CellOutput], -} - -impl<'a> MmapCell<'a> { - pub fn new(outputs: &'a [&'a CellOutput], input_cells: &'a [&'a CellOutput]) -> MmapCell<'a> { - MmapCell { - outputs, - input_cells, - } - } - - fn read_data(&self, source: Source, index: usize) -> Option<&'a [u8]> { - match source { - Source::INPUT => self.input_cells.get(index).map(|output| &output.data[..]), - Source::OUTPUT => self.outputs.get(index).map(|output| &output.data[..]), - } - } -} - -impl<'a, R: Register, M: Memory> Syscalls for MmapCell<'a> { - fn initialize(&mut self, _machine: &mut CoreMachine) -> Result<(), VMError> { - Ok(()) - } - - fn ecall(&mut self, machine: &mut CoreMachine) -> Result { - if machine.registers()[A7].to_u64() != MMAP_CELL_SYSCALL_NUMBER { - return Ok(false); - } - - let addr = machine.registers()[A0].to_usize(); - let size_addr = machine.registers()[A1].to_usize(); - let mode = Mode::parse_from_flag(machine.registers()[A2].to_u64())?; - - let index = machine.registers()[A4].to_usize(); - let source = Source::parse_from_u64(machine.registers()[A5].to_u64())?; - let size = machine.memory_mut().load64(size_addr)? as usize; - - let data = self - .read_data(source, index) - .ok_or_else(|| VMError::ParseError)?; - - let (size, offset) = match mode { - Mode::ALL => { - if size < data.len() { - machine.memory_mut().store64(size_addr, data.len() as u64)?; - machine.registers_mut()[A0] = R::from_u8(OVERRIDE_LEN); - } else { - machine.registers_mut()[A0] = R::from_u8(SUCCESS); - } - (data.len(), 0) - } - Mode::PARTIAL => { - let offset = machine.registers()[A3].to_usize(); - let real_size = cmp::min(size, data.len() - offset); - machine.memory_mut().store64(size_addr, real_size as u64)?; - machine.registers_mut()[A0] = R::from_u8(SUCCESS); - (real_size, offset) - } - }; - - machine.memory_mut().mmap( - addr, - size, - PROT_READ, - Some(Rc::new(data.to_vec().into_boxed_slice())), - offset, - )?; - Ok(true) - } -} diff --git a/script/src/syscalls/mmap_tx.rs b/script/src/syscalls/mmap_tx.rs deleted file mode 100644 index 6127a5be31..0000000000 --- a/script/src/syscalls/mmap_tx.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::syscalls::{Mode, MMAP_TX_SYSCALL_NUMBER, OVERRIDE_LEN, SUCCESS}; -use ckb_vm::memory::PROT_READ; -use ckb_vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A7}; -use std::cmp; -use std::rc::Rc; - -pub struct MmapTx<'a> { - tx: &'a [u8], -} - -impl<'a> MmapTx<'a> { - pub fn new(tx: &'a [u8]) -> MmapTx<'a> { - MmapTx { tx } - } -} - -impl<'a, R: Register, M: Memory> Syscalls for MmapTx<'a> { - fn initialize(&mut self, _machine: &mut CoreMachine) -> Result<(), VMError> { - Ok(()) - } - - fn ecall(&mut self, machine: &mut CoreMachine) -> Result { - if machine.registers()[A7].to_u64() != MMAP_TX_SYSCALL_NUMBER { - return Ok(false); - } - - let addr = machine.registers()[A0].to_usize(); - let size_addr = machine.registers()[A1].to_usize(); - let mode = Mode::parse_from_flag(machine.registers()[A2].to_u64())?; - let size = machine.memory_mut().load64(size_addr)? as usize; - - let data = self.tx; - - let (size, offset) = match mode { - Mode::ALL => { - if size < data.len() { - machine.memory_mut().store64(size_addr, data.len() as u64)?; - machine.registers_mut()[A0] = R::from_u8(OVERRIDE_LEN); - } else { - machine.registers_mut()[A0] = R::from_u8(SUCCESS); - } - (data.len(), 0) - } - Mode::PARTIAL => { - let offset = machine.registers()[A3].to_usize(); - let real_size = cmp::min(size, data.len() - offset); - machine.memory_mut().store64(size_addr, real_size as u64)?; - machine.registers_mut()[A0] = R::from_u8(SUCCESS); - (real_size, offset) - } - }; - - machine.memory_mut().mmap( - addr, - size, - PROT_READ, - Some(Rc::new(data.to_vec().into_boxed_slice())), - offset, - )?; - Ok(true) - } -} diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 7e03828153..4f0e2aa8f7 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -1,54 +1,42 @@ mod builder; mod debugger; -mod fetch_script_hash; -mod mmap_cell; -mod mmap_tx; +mod load_cell; +mod load_cell_by_field; +mod load_tx; pub use self::builder::build_tx; pub use self::debugger::Debugger; -pub use self::fetch_script_hash::FetchScriptHash; -pub use self::mmap_cell::MmapCell; -pub use self::mmap_tx::MmapTx; +pub use self::load_cell::LoadCell; +pub use self::load_cell_by_field::LoadCellByField; +pub use self::load_tx::LoadTx; use ckb_vm::Error; pub const SUCCESS: u8 = 0; -pub const OVERRIDE_LEN: u8 = 1; pub const ITEM_MISSING: u8 = 2; -pub const MMAP_TX_SYSCALL_NUMBER: u64 = 2049; -pub const MMAP_CELL_SYSCALL_NUMBER: u64 = 2050; -pub const FETCH_SCRIPT_HASH_SYSCALL_NUMBER: u64 = 2051; -pub const FETCH_CURRENT_SCRIPT_HASH_SYSCALL_NUMBER: u64 = 2052; +pub const LOAD_TX_SYSCALL_NUMBER: u64 = 2049; +pub const LOAD_CELL_SYSCALL_NUMBER: u64 = 2053; +pub const LOAD_CELL_BY_FIELD_SYSCALL_NUMBER: u64 = 2054; pub const DEBUG_PRINT_SYSCALL_NUMBER: u64 = 2177; #[derive(Debug, PartialEq, Clone, Copy, Eq)] -pub enum Mode { - ALL, - PARTIAL, +enum Field { + Capacity, + Data, + LockHash, + Contract, + ContractHash, } -impl Mode { - pub fn parse_from_flag(flag: u64) -> Result { - match flag { - 0 => Ok(Mode::ALL), - 1 => Ok(Mode::PARTIAL), - _ => Err(Error::ParseError), - } - } -} - -#[derive(Debug, PartialEq, Clone, Copy, Eq)] -pub enum Category { - LOCK, - CONTRACT, -} - -impl Category { - pub fn parse_from_u64(i: u64) -> Result { +impl Field { + fn parse_from_u64(i: u64) -> Result { match i { - 0 => Ok(Category::LOCK), - 1 => Ok(Category::CONTRACT), + 0 => Ok(Field::Capacity), + 1 => Ok(Field::Data), + 2 => Ok(Field::LockHash), + 3 => Ok(Field::Contract), + 4 => Ok(Field::ContractHash), _ => Err(Error::ParseError), } } @@ -56,15 +44,17 @@ impl Category { #[derive(Debug, PartialEq, Clone, Copy, Eq)] enum Source { - INPUT, - OUTPUT, + Input, + Output, + Current, } impl Source { fn parse_from_u64(i: u64) -> Result { match i { - 0 => Ok(Source::INPUT), - 1 => Ok(Source::OUTPUT), + 0 => Ok(Source::Input), + 1 => Ok(Source::Output), + 2 => Ok(Source::Current), _ => Err(Error::ParseError), } } @@ -73,71 +63,50 @@ impl Source { #[cfg(test)] mod tests { use super::*; - use ckb_core::script::Script; - use ckb_core::transaction::{CellInput, CellOutput, OutPoint}; + use byteorder::{LittleEndian, WriteBytesExt}; + use ckb_core::transaction::CellOutput; + use ckb_protocol::CellOutput as FbsCellOutput; use ckb_vm::machine::DefaultCoreMachine; use ckb_vm::{ CoreMachine, Error as VMError, Memory, SparseMemory, Syscalls, A0, A1, A2, A3, A4, A5, A7, }; + use flatbuffers::FlatBufferBuilder; + use hash::sha3_256; use numext_fixed_hash::H256; - use proptest::{collection::size_range, prelude::any_with, proptest, proptest_helper}; + use proptest::{collection::size_range, prelude::any, prelude::any_with, proptest, proptest_helper}; - fn _test_mmap_tx_all(tx: &Vec) { + fn _test_load_tx_all(tx: &Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // mode: all - machine.registers_mut()[A7] = MMAP_TX_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A7] = LOAD_TX_SYSCALL_NUMBER; // syscall number assert!(machine .memory_mut() .store64(size_addr as usize, tx.len() as u64) .is_ok()); - let mut mmap_tx = MmapTx::new(tx); - assert!(mmap_tx.ecall(&mut machine).is_ok()); + let mut load_tx = LoadTx::new(tx); + assert!(load_tx.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); for (i, addr) in (addr as usize..addr as usize + tx.len()).enumerate() { assert_eq!(machine.memory_mut().load8(addr), Ok(tx[i])) } - - // clean memory - assert!(machine.memory_mut().munmap(0, 1100).is_ok()); - - // test all mode execute with wrong len - // reset register A0 - machine.registers_mut()[A0] = addr; // addr - let len = tx.len() as u64 - 100; - - // write len - 100 - assert!(machine - .memory_mut() - .store64(size_addr as usize, len) - .is_ok()); - - assert!(mmap_tx.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], OVERRIDE_LEN as u64); - assert_eq!( - machine.memory_mut().load64(size_addr as usize), - Ok(tx.len() as u64) - ); - for (i, addr) in (addr as usize..addr as usize + tx.len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(tx[i])) - } } proptest! { #[test] - fn test_mmap_tx_all(ref tx in any_with::>(size_range(1000).lift())) { - _test_mmap_tx_all(tx); + fn test_load_tx_all(ref tx in any_with::>(size_range(1000).lift())) { + _test_load_tx_all(tx); } } - fn _test_mmap_tx_partial(tx: &Vec) { + fn _test_load_tx_partial(tx: &Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; @@ -145,17 +114,16 @@ mod tests { machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 1; // mode: partial - machine.registers_mut()[A3] = offset as u64; // offset - machine.registers_mut()[A7] = MMAP_TX_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = offset as u64; // offset + machine.registers_mut()[A7] = LOAD_TX_SYSCALL_NUMBER; // syscall number assert!(machine .memory_mut() .store64(size_addr as usize, tx.len() as u64) .is_ok()); - let mut mmap_tx = MmapTx::new(tx); - assert!(mmap_tx.ecall(&mut machine).is_ok()); + let mut load_tx = LoadTx::new(tx); + assert!(load_tx.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); assert_eq!( @@ -169,22 +137,22 @@ mod tests { proptest! { #[test] - fn test_mmap_tx_partial(ref tx in any_with::>(size_range(1000).lift())) { - _test_mmap_tx_partial(tx); + fn test_load_tx_partial(ref tx in any_with::>(size_range(1000).lift())) { + _test_load_tx_partial(tx); } } - fn _test_mmap_cell_out_of_bound(data: Vec) { + fn _test_load_cell_out_of_bound(data: Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // mode: all - machine.registers_mut()[A4] = 1; //index - machine.registers_mut()[A5] = 0; //source: 0 input - machine.registers_mut()[A7] = MMAP_CELL_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A3] = 1; //index + machine.registers_mut()[A4] = 0; //source: 0 input + machine.registers_mut()[A7] = LOAD_CELL_SYSCALL_NUMBER; // syscall number assert!(machine .memory_mut() @@ -200,34 +168,29 @@ mod tests { ); let outputs = vec![&output]; let input_cells = vec![&input_cell]; - let mut mmap_cell = MmapCell::new(&outputs, &input_cells); + let mut load_cell = LoadCell::new(&outputs, &input_cells, &input_cell); - assert_eq!(mmap_cell.ecall(&mut machine), Err(VMError::ParseError)); // index out of bounds + assert_eq!(load_cell.ecall(&mut machine), Err(VMError::OutOfBound)); // index out of bounds } proptest! { #[test] - fn test_mmap_cell_out_of_bound(data in any_with::>(size_range(1000).lift())) { - _test_mmap_cell_out_of_bound(data); + fn test_load_cell_out_of_bound(data in any_with::>(size_range(1000).lift())) { + _test_load_cell_out_of_bound(data); } } - fn _test_mmap_cell_all(data: Vec) { + fn _test_load_cell_all(data: Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // mode: all - machine.registers_mut()[A4] = 0; //index - machine.registers_mut()[A5] = 0; //source: 0 input - machine.registers_mut()[A7] = MMAP_CELL_SYSCALL_NUMBER; // syscall number - - assert!(machine - .memory_mut() - .store64(size_addr as usize, data.len() as u64) - .is_ok()); + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A3] = 0; //index + machine.registers_mut()[A4] = 0; //source: 0 input + machine.registers_mut()[A7] = LOAD_CELL_SYSCALL_NUMBER; // syscall number let output = CellOutput::new(100, data.clone(), H256::zero(), None); let input_cell = CellOutput::new( @@ -238,55 +201,69 @@ mod tests { ); let outputs = vec![&output]; let input_cells = vec![&input_cell]; - let mut mmap_cell = MmapCell::new(&outputs, &input_cells); + let mut load_cell = LoadCell::new(&outputs, &input_cells, &input_cell); - // test input - assert!(mmap_cell.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], SUCCESS as u64); + let mut builder = FlatBufferBuilder::new(); + let fbs_offset = FbsCellOutput::build(&mut builder, &input_cell); + builder.finish(fbs_offset, None); + let input_correct_data = builder.finished_data(); - for (i, addr) in (addr as usize..addr as usize + data.len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(input_cell.data[i])) - } + let mut builder = FlatBufferBuilder::new(); + let fbs_offset = FbsCellOutput::build(&mut builder, &output); + builder.finish(fbs_offset, None); + let output_correct_data = builder.finished_data(); - // clean memory - assert!(machine.memory_mut().munmap(0, 1100).is_ok()); + // test input + assert!(machine + .memory_mut() + .store64(size_addr as usize, input_correct_data.len() as u64) + .is_ok()); - // test output - machine.registers_mut()[A0] = addr; // addr - machine.registers_mut()[A5] = 1; //source: 1 output - assert!(mmap_cell.ecall(&mut machine).is_ok()); + assert!(load_cell.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); - for (i, addr) in (addr as usize..addr as usize + data.len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(output.data[i])) + assert_eq!( + machine.memory_mut().load64(size_addr as usize), + Ok(input_correct_data.len() as u64) + ); + + for (i, addr) in (addr as usize..addr as usize + input_correct_data.len()).enumerate() { + assert_eq!(machine.memory_mut().load8(addr), Ok(input_correct_data[i])) } // clean memory - // test all mode execute with wrong len - // write len - 100 - assert!(machine.memory_mut().munmap(0, 1100).is_ok()); + assert!(machine.memory_mut().store_byte(0, 1100, 0).is_ok()); + + // test output machine.registers_mut()[A0] = addr; // addr - let len = data.len() as u64 - 100; + machine.registers_mut()[A1] = size_addr; // size_addr + machine.registers_mut()[A4] = 1; //source: 1 output assert!(machine .memory_mut() - .store64(size_addr as usize, len) + .store64(size_addr as usize, output_correct_data.len() as u64 + 10) .is_ok()); - assert!(mmap_cell.ecall(&mut machine).is_ok()); + + assert!(load_cell.ecall(&mut machine).is_ok()); + assert_eq!(machine.registers()[A0], SUCCESS as u64); + assert_eq!( machine.memory_mut().load64(size_addr as usize), - Ok((data.len()) as u64) + Ok(output_correct_data.len() as u64) ); - assert_eq!(machine.registers()[A0], OVERRIDE_LEN as u64); + + for (i, addr) in (addr as usize..addr as usize + output_correct_data.len()).enumerate() { + assert_eq!(machine.memory_mut().load8(addr), Ok(output_correct_data[i])) + } } proptest! { #[test] - fn test_mmap_cell_all(tx in any_with::>(size_range(1000).lift())) { - _test_mmap_cell_all(tx); + fn test_load_cell_all(tx in any_with::>(size_range(1000).lift())) { + _test_load_cell_all(tx); } } - fn _test_mmap_cell_partial(data: Vec) { + fn _test_load_cell_partial(data: Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; @@ -294,16 +271,10 @@ mod tests { machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 1; // mode: partial - machine.registers_mut()[A3] = offset as u64; // offset - machine.registers_mut()[A4] = 0; // index - machine.registers_mut()[A5] = 0; // source: 0 input - machine.registers_mut()[A7] = MMAP_CELL_SYSCALL_NUMBER; // syscall number - - assert!(machine - .memory_mut() - .store64(size_addr as usize, data.len() as u64) - .is_ok()); + machine.registers_mut()[A2] = offset as u64; // offset + machine.registers_mut()[A3] = 0; // index + machine.registers_mut()[A4] = 0; // source: 0 input + machine.registers_mut()[A7] = LOAD_CELL_SYSCALL_NUMBER; // syscall number let output = CellOutput::new(100, data.clone(), H256::zero(), None); let input_cell = CellOutput::new( @@ -314,311 +285,203 @@ mod tests { ); let outputs = vec![&output]; let input_cells = vec![&input_cell]; - let mut mmap_cell = MmapCell::new(&outputs, &input_cells); + let mut load_cell = LoadCell::new(&outputs, &input_cells, &input_cell); + + let mut builder = FlatBufferBuilder::new(); + let fbs_offset = FbsCellOutput::build(&mut builder, &input_cell); + builder.finish(fbs_offset, None); + let input_correct_data = builder.finished_data(); - assert!(mmap_cell.ecall(&mut machine).is_ok()); + assert!(machine + .memory_mut() + .store64(size_addr as usize, input_correct_data.len() as u64) + .is_ok()); + + assert!(load_cell.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); - for (i, addr) in (addr as usize..addr as usize + data.len() - offset).enumerate() { + for (i, addr) in + (addr as usize..addr as usize + input_correct_data.len() - offset).enumerate() + { assert_eq!( machine.memory_mut().load8(addr), - Ok(input_cell.data[i + offset]) + Ok(input_correct_data[i + offset]) ) } } proptest! { #[test] - fn test_mmap_cell_partial(data in any_with::>(size_range(1000).lift())) { - _test_mmap_cell_partial(data); + fn test_load_cell_partial(data in any_with::>(size_range(1000).lift())) { + _test_load_cell_partial(data); } } - fn _test_fetch_script_hash_input_lock(data: Vec) { + fn _test_load_current_cell(data: Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 0; // source: 0 input - machine.registers_mut()[A4] = 0; // category: 0 lock - machine.registers_mut()[A7] = FETCH_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A3] = 1000; //index + machine.registers_mut()[A4] = 2; //source: 2 self + machine.registers_mut()[A7] = LOAD_CELL_SYSCALL_NUMBER; // syscall number - assert!(machine.memory_mut().store64(size_addr as usize, 32).is_ok()); + let input_cell = CellOutput::new( + 100, + data.iter().rev().cloned().collect(), + H256::zero(), + None, + ); + let outputs = vec![]; + let input_cells = vec![]; + let mut load_cell = LoadCell::new(&outputs, &input_cells, &input_cell); - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let input = CellInput::new(OutPoint::default(), script.clone()); - let inputs = vec![&input]; - let input_cells = Vec::new(); - let outputs = Vec::new(); + let mut builder = FlatBufferBuilder::new(); + let fbs_offset = FbsCellOutput::build(&mut builder, &input_cell); + builder.finish(fbs_offset, None); + let input_correct_data = builder.finished_data(); - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::zero()); + // test input + assert!(machine + .memory_mut() + .store64(size_addr as usize, input_correct_data.len() as u64 + 5) + .is_ok()); - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); + assert!(load_cell.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); - let hash = &script.type_hash(); - for (i, addr) in (addr as usize..addr as usize + hash.as_bytes().len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(hash.as_bytes()[i])) - } - } - - proptest! { - #[test] - fn test_fetch_script_hash_input_lock(data in any_with::>(size_range(1000).lift())) { - _test_fetch_script_hash_input_lock(data); - } - } - - fn _test_fetch_script_hash_input_contract(data: Vec) { - let mut machine = DefaultCoreMachine::::default(); - let size_addr = 0; - let addr = 100; - - machine.registers_mut()[A0] = addr; // addr - machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 0; // source: 0 input - machine.registers_mut()[A4] = 1; // category: 1 contract - machine.registers_mut()[A7] = FETCH_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number - - assert!(machine.memory_mut().store64(size_addr as usize, 32).is_ok()); - - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let output = CellOutput::new(0, Vec::new(), H256::zero(), Some(script.clone())); - let inputs = Vec::new(); - let input_cells = vec![&output]; - let outputs = Vec::new(); - - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::zero()); - - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], SUCCESS as u64); + assert_eq!( + machine.memory_mut().load64(size_addr as usize), + Ok(input_correct_data.len() as u64) + ); - let hash = &script.type_hash(); - for (i, addr) in (addr as usize..addr as usize + hash.as_bytes().len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(hash.as_bytes()[i])) + for (i, addr) in (addr as usize..addr as usize + input_correct_data.len()).enumerate() { + assert_eq!(machine.memory_mut().load8(addr), Ok(input_correct_data[i])) } } proptest! { #[test] - fn test_fetch_script_hash_input_contract(data in any_with::>(size_range(1000).lift())) { - _test_fetch_script_hash_input_contract(data); + fn test_load_current_cell(tx in any_with::>(size_range(1000).lift())) { + _test_load_current_cell(tx); } } - fn _test_fetch_script_hash_output_lock(data: Vec) { + fn _test_load_cell_capacity(capacity: u64) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 1; // source: 1 output - machine.registers_mut()[A4] = 0; // category: 0 lock - machine.registers_mut()[A7] = FETCH_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A3] = 0; //index + machine.registers_mut()[A4] = 0; //source: 0 input + machine.registers_mut()[A5] = 0; //field: 0 capacity + machine.registers_mut()[A7] = LOAD_CELL_BY_FIELD_SYSCALL_NUMBER; // syscall number + + let input_cell = CellOutput::new(capacity, vec![], H256::zero(), None); + let outputs = vec![]; + let input_cells = vec![&input_cell]; + let mut load_cell = LoadCellByField::new(&outputs, &input_cells, &input_cell); - assert!(machine.memory_mut().store64(size_addr as usize, 32).is_ok()); + assert!(machine.memory_mut().store64(size_addr as usize, 16).is_ok()); - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let output = CellOutput::new(0, Vec::new(), script.type_hash(), None); - let inputs = Vec::new(); - let input_cells = Vec::new(); - let outputs = vec![&output]; + assert!(load_cell.ecall(&mut machine).is_ok()); + assert_eq!(machine.registers()[A0], SUCCESS as u64); - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::zero()); + assert_eq!(machine.memory_mut().load64(size_addr as usize), Ok(8)); - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], SUCCESS as u64); + let mut buffer = vec![]; + buffer.write_u64::(capacity).unwrap(); - let hash = &script.type_hash(); - for (i, addr) in (addr as usize..addr as usize + hash.as_bytes().len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(hash.as_bytes()[i])) + for (i, addr) in (addr as usize..addr as usize + buffer.len() as usize).enumerate() { + assert_eq!(machine.memory_mut().load8(addr), Ok(buffer[i])) } } proptest! { #[test] - fn test_fetch_script_hash_output_lock(data in any_with::>(size_range(1000).lift())) { - _test_fetch_script_hash_output_lock(data); + fn test_load_cell_capacity(capacity in any::()) { + _test_load_cell_capacity(capacity); } } - fn _test_fetch_script_hash_output_contract(data: Vec) { + fn _test_load_self_lock_hash(data: Vec) { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 1; // source: 1 output - machine.registers_mut()[A4] = 1; // category: 1 contract - machine.registers_mut()[A7] = FETCH_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A3] = 0; //index + machine.registers_mut()[A4] = 2; //source: 2 self + machine.registers_mut()[A5] = 2; //field: 2 lock hash + machine.registers_mut()[A7] = LOAD_CELL_BY_FIELD_SYSCALL_NUMBER; // syscall number - assert!(machine.memory_mut().store64(size_addr as usize, 32).is_ok()); + let sha3_data = sha3_256(data); + let input_cell = CellOutput::new(100, vec![], H256::from_slice(&sha3_data).unwrap(), None); + let outputs = vec![]; + let input_cells = vec![]; + let mut load_cell = LoadCellByField::new(&outputs, &input_cells, &input_cell); - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let output = CellOutput::new(0, Vec::new(), H256::zero(), Some(script.clone())); - let inputs = Vec::new(); - let input_cells = Vec::new(); - let outputs = vec![&output]; - - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::zero()); + assert!(machine.memory_mut().store64(size_addr as usize, 64).is_ok()); - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); + assert!(load_cell.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); - let hash = &script.type_hash(); - for (i, addr) in (addr as usize..addr as usize + hash.as_bytes().len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(hash.as_bytes()[i])) - } - } + assert_eq!( + machine.memory_mut().load64(size_addr as usize), + Ok(sha3_data.len() as u64) + ); - proptest! { - #[test] - fn test_fetch_script_hash_output_contract(data in any_with::>(size_range(1000).lift())) { - _test_fetch_script_hash_output_contract(data); + for (i, addr) in (addr as usize..addr as usize + sha3_data.len() as usize).enumerate() { + assert_eq!(machine.memory_mut().load8(addr), Ok(sha3_data[i])) } } - fn _test_fetch_script_hash_not_enough_space(data: Vec) { - let mut machine = DefaultCoreMachine::::default(); - let size_addr = 0; - let addr = 100; - - machine.registers_mut()[A0] = addr; // addr - machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 1; // source: 1 output - machine.registers_mut()[A4] = 1; // category: 1 contract - machine.registers_mut()[A7] = FETCH_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number - - assert!(machine.memory_mut().store64(size_addr as usize, 16).is_ok()); - - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let output = CellOutput::new(0, Vec::new(), H256::zero(), Some(script.clone())); - let inputs = Vec::new(); - let input_cells = Vec::new(); - let outputs = vec![&output]; - - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::zero()); - - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], OVERRIDE_LEN as u64); - - assert_eq!(machine.memory_mut().load64(size_addr as usize), Ok(32)); - } - proptest! { #[test] - fn test_fetch_script_hash_not_enough_space(data in any_with::>(size_range(1000).lift())) { - _test_fetch_script_hash_not_enough_space(data); + fn test_load_self_lock_hash(data in any_with::>(size_range(1000).lift())) { + _test_load_self_lock_hash(data); } } #[test] - fn test_fetch_script_hash_missing_item() { + fn test_load_missing_contract() { let mut machine = DefaultCoreMachine::::default(); let size_addr = 0; let addr = 100; machine.registers_mut()[A0] = addr; // addr machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 1; // source: 1 output - machine.registers_mut()[A4] = 1; // category: 1 contract - machine.registers_mut()[A7] = FETCH_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number + machine.registers_mut()[A2] = 0; // offset + machine.registers_mut()[A3] = 0; //index + machine.registers_mut()[A4] = 1; //source: 1 output + machine.registers_mut()[A5] = 3; //field: 3 contract + machine.registers_mut()[A7] = LOAD_CELL_BY_FIELD_SYSCALL_NUMBER; // syscall number - assert!(machine.memory_mut().store64(size_addr as usize, 16).is_ok()); + let output_cell = CellOutput::new(100, vec![], H256::default(), None); + let outputs = vec![&output_cell]; + let input_cells = vec![]; + let mut load_cell = LoadCellByField::new(&outputs, &input_cells, &output_cell); - let inputs = Vec::new(); - let input_cells = Vec::new(); - let outputs = Vec::new(); - - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::zero()); + assert!(machine + .memory_mut() + .store64(size_addr as usize, 100) + .is_ok()); - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); + assert!(load_cell.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], ITEM_MISSING as u64); - } - - fn _test_fetch_current_script_hash(data: Vec) { - let mut machine = DefaultCoreMachine::::default(); - let size_addr = 0; - let addr = 100; - - machine.registers_mut()[A0] = addr; // addr - machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A7] = FETCH_CURRENT_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number - - assert!(machine.memory_mut().store64(size_addr as usize, 32).is_ok()); - - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let inputs = Vec::new(); - let input_cells = Vec::new(); - let outputs = Vec::new(); - - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, script.type_hash()); - - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], SUCCESS as u64); - - let hash = &script.type_hash(); - for (i, addr) in (addr as usize..addr as usize + hash.as_bytes().len()).enumerate() { - assert_eq!(machine.memory_mut().load8(addr), Ok(hash.as_bytes()[i])) - } - } - proptest! { - #[test] - fn test_fetch_current_script_hash(data in any_with::>(size_range(1000).lift())) { - _test_fetch_current_script_hash(data); - } - } - - fn _test_fetch_current_script_hash_not_enough_space(data: Vec) { - let mut machine = DefaultCoreMachine::::default(); - let size_addr = 0; - let addr = 100; - - machine.registers_mut()[A0] = addr; // addr - machine.registers_mut()[A1] = size_addr; // size_addr - machine.registers_mut()[A2] = 0; // index - machine.registers_mut()[A3] = 1; // source: 1 output - machine.registers_mut()[A4] = 1; // category: 1 contract - machine.registers_mut()[A7] = FETCH_CURRENT_SCRIPT_HASH_SYSCALL_NUMBER; // syscall number - - assert!(machine.memory_mut().store64(size_addr as usize, 16).is_ok()); - - let script = Script::new(0, Vec::new(), None, Some(data), Vec::new()); - let inputs = Vec::new(); - let input_cells = Vec::new(); - let outputs = Vec::new(); - - let mut fetch_script_hash = - FetchScriptHash::new(&outputs, &inputs, &input_cells, script.type_hash()); + assert_eq!(machine.memory_mut().load64(size_addr as usize), Ok(100)); - assert!(fetch_script_hash.ecall(&mut machine).is_ok()); - assert_eq!(machine.registers()[A0], OVERRIDE_LEN as u64); - - assert_eq!(machine.memory_mut().load64(size_addr as usize), Ok(32)); - } - - proptest! { - #[test] - fn test_fetch_current_script_hash_not_enough_space(data in any_with::>(size_range(1000).lift())) { - _test_fetch_current_script_hash_not_enough_space(data); + for addr in addr as usize..addr as usize + 100 { + assert_eq!(machine.memory_mut().load8(addr), Ok(0)) } } } diff --git a/script/src/verify.rs b/script/src/verify.rs index 425427a005..e866870a8e 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -8,6 +8,7 @@ use flatbuffers::FlatBufferBuilder; use fnv::FnvHashMap; use log::info; use numext_fixed_hash::H256; +use syscalls::{build_tx, Debugger, LoadCell, LoadCellByField, LoadTx}; // This struct leverages CKB VM to verify transaction inputs. // FlatBufferBuilder owned Vec that grows as needed, in the @@ -61,21 +62,16 @@ impl<'a> TransactionScriptsVerifier<'a> { } } - fn build_mmap_tx(&self) -> MmapTx { - MmapTx::new(self.tx_builder.finished_data()) + fn build_load_tx(&self) -> LoadTx { + LoadTx::new(self.tx_builder.finished_data()) } - fn build_mmap_cell(&self) -> MmapCell { - MmapCell::new(&self.outputs, &self.input_cells) + fn build_load_cell(&self, current_cell: &'a CellOutput) -> LoadCell { + LoadCell::new(&self.outputs, &self.input_cells, current_cell) } - fn build_fetch_script_hash(&self, current_script_hash: H256) -> FetchScriptHash { - FetchScriptHash::new( - &self.outputs, - &self.inputs, - &self.input_cells, - current_script_hash, - ) + fn build_load_cell_by_field(&self, current_cell: &'a CellOutput) -> LoadCellByField { + LoadCellByField::new(&self.outputs, &self.input_cells, current_cell) } // Script struct might contain references to external cells, this @@ -93,16 +89,21 @@ impl<'a> TransactionScriptsVerifier<'a> { Err(ScriptError::NoScript) } - pub fn verify_script(&self, script: &Script, prefix: &str) -> Result<(), ScriptError> { + pub fn verify_script( + &self, + script: &Script, + prefix: &str, + current_cell: &'a CellOutput, + ) -> Result<(), ScriptError> { self.extract_script(script).and_then(|script_binary| { let mut args = vec![b"verify".to_vec()]; args.extend_from_slice(&script.signed_args.as_slice()); args.extend_from_slice(&script.args.as_slice()); let mut machine = DefaultMachine::::default(); - machine.add_syscall_module(Box::new(self.build_mmap_tx())); - machine.add_syscall_module(Box::new(self.build_mmap_cell())); - machine.add_syscall_module(Box::new(self.build_fetch_script_hash(script.type_hash()))); + machine.add_syscall_module(Box::new(self.build_load_tx())); + machine.add_syscall_module(Box::new(self.build_load_cell(current_cell))); + machine.add_syscall_module(Box::new(self.build_load_cell_by_field(current_cell))); machine.add_syscall_module(Box::new(Debugger::new(prefix))); machine .run(script_binary, &args) @@ -120,7 +121,7 @@ impl<'a> TransactionScriptsVerifier<'a> { pub fn verify(&self) -> Result<(), ScriptError> { for (i, input) in self.inputs.iter().enumerate() { let prefix = format!("Transaction {}, input {}", self.hash, i); - self.verify_script(&input.unlock, &prefix).map_err(|e| { + self.verify_script(&input.unlock, &prefix, self.input_cells[i]).map_err(|e| { info!(target: "script", "Error validating input {} of transaction {}: {:?}", i, self.hash, e); e })?; @@ -128,7 +129,7 @@ impl<'a> TransactionScriptsVerifier<'a> { for (i, output) in self.outputs.iter().enumerate() { if let Some(ref contract) = output.contract { let prefix = format!("Transaction {}, output {}", self.hash, i); - self.verify_script(contract, &prefix).map_err(|e| { + self.verify_script(contract, &prefix, output).map_err(|e| { info!(target: "script", "Error validating output {} of transaction {}: {:?}", i, self.hash, e); e })?; @@ -200,10 +201,12 @@ mod tests { let transaction = TransactionBuilder::default().input(input.clone()).build(); + let dummy_cell = CellOutput::new(100, vec![], H256::default(), None); + let rtx = ResolvedTransaction { transaction, dep_cells: vec![], - input_cells: vec![], + input_cells: vec![CellStatus::Current(dummy_cell)], }; let verifier = TransactionScriptsVerifier::new(&rtx); @@ -245,10 +248,12 @@ mod tests { let transaction = TransactionBuilder::default().input(input.clone()).build(); + let dummy_cell = CellOutput::new(100, vec![], H256::default(), None); + let rtx = ResolvedTransaction { transaction, dep_cells: vec![], - input_cells: vec![], + input_cells: vec![CellStatus::Current(dummy_cell)], }; let verifier = TransactionScriptsVerifier::new(&rtx); @@ -295,10 +300,12 @@ mod tests { .dep(dep_outpoint.clone()) .build(); + let dummy_cell = CellOutput::new(100, vec![], H256::default(), None); + let rtx = ResolvedTransaction { transaction, dep_cells: vec![CellStatus::Current(dep_cell.clone())], - input_cells: vec![], + input_cells: vec![CellStatus::Current(dummy_cell)], }; let verifier = TransactionScriptsVerifier::new(&rtx); @@ -348,10 +355,12 @@ mod tests { .dep(dep_outpoint) .build(); + let dummy_cell = CellOutput::new(100, vec![], H256::default(), None); + let rtx = ResolvedTransaction { transaction, dep_cells: vec![], - input_cells: vec![], + input_cells: vec![CellStatus::Current(dummy_cell)], }; let verifier = TransactionScriptsVerifier::new(&rtx); @@ -403,10 +412,12 @@ mod tests { .output(output.clone()) .build(); + let dummy_cell = CellOutput::new(100, vec![], H256::default(), None); + let rtx = ResolvedTransaction { transaction, dep_cells: vec![], - input_cells: vec![], + input_cells: vec![CellStatus::Current(dummy_cell)], }; let verifier = TransactionScriptsVerifier::new(&rtx); @@ -452,10 +463,12 @@ mod tests { .output(output.clone()) .build(); + let dummy_cell = CellOutput::new(100, vec![], H256::default(), None); + let rtx = ResolvedTransaction { transaction, dep_cells: vec![], - input_cells: vec![], + input_cells: vec![CellStatus::Current(dummy_cell)], }; let verifier = TransactionScriptsVerifier::new(&rtx);