diff --git a/script/src/syscalls/fetch_script_hash.rs b/script/src/syscalls/fetch_script_hash.rs index c7e79f9b7e..64ecc0a136 100644 --- a/script/src/syscalls/fetch_script_hash.rs +++ b/script/src/syscalls/fetch_script_hash.rs @@ -2,7 +2,8 @@ use bigint::H256; use ckb_core::transaction::{CellInput, CellOutput}; use ckb_vm::{CoreMachine, Error as VMError, Memory, Register, Syscalls, A0, A1, A2, A3, A4, A7}; use syscalls::{ - Category, Source, FETCH_SCRIPT_HASH_SYSCALL_NUMBER, ITEM_MISSING, OVERRIDE_LEN, SUCCESS, + Category, Source, FETCH_CURRENT_SCRIPT_HASH_SYSCALL_NUMBER, FETCH_SCRIPT_HASH_SYSCALL_NUMBER, + ITEM_MISSING, OVERRIDE_LEN, SUCCESS, }; #[derive(Debug)] @@ -10,6 +11,7 @@ pub struct FetchScriptHash<'a> { outputs: &'a [&'a CellOutput], inputs: &'a [&'a CellInput], input_cells: &'a [&'a CellOutput], + current_script_hash: H256, } impl<'a> FetchScriptHash<'a> { @@ -17,11 +19,13 @@ impl<'a> FetchScriptHash<'a> { outputs: &'a [&'a CellOutput], inputs: &'a [&'a CellInput], input_cells: &'a [&'a CellOutput], + current_script_hash: H256, ) -> FetchScriptHash<'a> { FetchScriptHash { outputs, inputs, input_cells, + current_script_hash, } } @@ -55,7 +59,10 @@ impl<'a, R: Register, M: Memory> Syscalls for FetchScriptHash<'a> { } fn ecall(&mut self, machine: &mut CoreMachine) -> Result { - if machine.registers()[A7].to_u64() != FETCH_SCRIPT_HASH_SYSCALL_NUMBER { + let code = machine.registers()[A7].to_u64(); + if code != FETCH_SCRIPT_HASH_SYSCALL_NUMBER + && code != FETCH_CURRENT_SCRIPT_HASH_SYSCALL_NUMBER + { return Ok(false); } @@ -63,11 +70,16 @@ impl<'a, R: Register, M: Memory> Syscalls for FetchScriptHash<'a> { let size_addr = machine.registers()[A1].to_usize(); let size = machine.memory_mut().load64(size_addr)? as usize; - let index = machine.registers()[A2].to_usize(); - let source = Source::parse_from_u64(machine.registers()[A3].to_u64())?; - let category = Category::parse_from_u64(machine.registers()[A4].to_u64())?; + let hash = if code == FETCH_SCRIPT_HASH_SYSCALL_NUMBER { + let index = machine.registers()[A2].to_usize(); + let source = Source::parse_from_u64(machine.registers()[A3].to_u64())?; + let category = Category::parse_from_u64(machine.registers()[A4].to_u64())?; + self.fetch_hash(source, category, index) + } else { + Some(self.current_script_hash) + }; - match self.fetch_hash(source, category, index) { + match hash { Some(hash) => { let hash: &[u8] = &hash; machine.memory_mut().store64(size_addr, hash.len() as u64)?; diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 6915894704..8886db48f1 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -19,6 +19,7 @@ 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 DEBUG_PRINT_SYSCALL_NUMBER: u64 = 2177; #[derive(Debug, PartialEq, Clone, Copy, Eq)] @@ -368,7 +369,8 @@ mod tests { let input_cells = Vec::new(); let outputs = Vec::new(); - let mut fetch_script_hash = FetchScriptHash::new(&outputs, &inputs, &input_cells); + let mut fetch_script_hash = + FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::from(0)); assert!(fetch_script_hash.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); @@ -406,7 +408,8 @@ mod tests { let input_cells = vec![&output]; let outputs = Vec::new(); - let mut fetch_script_hash = FetchScriptHash::new(&outputs, &inputs, &input_cells); + let mut fetch_script_hash = + FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::from(0)); assert!(fetch_script_hash.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); @@ -444,7 +447,8 @@ mod tests { let input_cells = Vec::new(); let outputs = vec![&output]; - let mut fetch_script_hash = FetchScriptHash::new(&outputs, &inputs, &input_cells); + let mut fetch_script_hash = + FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::from(0)); assert!(fetch_script_hash.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], SUCCESS as u64); @@ -482,7 +486,8 @@ mod tests { let input_cells = Vec::new(); let outputs = vec![&output]; - let mut fetch_script_hash = FetchScriptHash::new(&outputs, &inputs, &input_cells); + let mut fetch_script_hash = + FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::from(0)); assert!(fetch_script_hash.ecall(&mut machine).is_ok()); assert_eq!(machine.registers()[A0], OVERRIDE_LEN as u64); @@ -516,9 +521,80 @@ mod tests { let input_cells = Vec::new(); let outputs = Vec::new(); - let mut fetch_script_hash = FetchScriptHash::new(&outputs, &inputs, &input_cells); + let mut fetch_script_hash = + FetchScriptHash::new(&outputs, &inputs, &input_cells, H256::from(0)); assert!(fetch_script_hash.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.len()).enumerate() { + assert_eq!(machine.memory_mut().load8(addr), Ok(hash[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!(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); + } + } } diff --git a/script/src/verify.rs b/script/src/verify.rs index 5255c2d9ed..a334a83afb 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -66,8 +66,13 @@ impl<'a> TransactionScriptsVerifier<'a> { MmapCell::new(&self.outputs, &self.input_cells) } - fn build_fetch_script_hash(&self) -> FetchScriptHash { - FetchScriptHash::new(&self.outputs, &self.inputs, &self.input_cells) + fn build_fetch_script_hash(&self, current_script_hash: H256) -> FetchScriptHash { + FetchScriptHash::new( + &self.outputs, + &self.inputs, + &self.input_cells, + current_script_hash, + ) } // Script struct might contain references to external cells, this @@ -94,7 +99,7 @@ impl<'a> TransactionScriptsVerifier<'a> { 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())); + machine.add_syscall_module(Box::new(self.build_fetch_script_hash(script.type_hash()))); machine.add_syscall_module(Box::new(Debugger::new(prefix))); machine .run(script_binary, &args)