From 01ac46c7f0341c92834a612212c9dedb55ff2376 Mon Sep 17 00:00:00 2001 From: mohanson Date: Tue, 23 Mar 2021 17:23:51 +0800 Subject: [PATCH 01/14] Bump ckb-vm --- Cargo.lock | 28 ++++++++++- script/Cargo.toml | 4 +- script/src/cost_model.rs | 68 ++++++++++++++++++++------- script/src/ill_transaction_checker.rs | 51 ++++++++------------ script/src/syscalls/mod.rs | 53 ++++++++++++++++----- script/src/verify.rs | 18 ++++--- 6 files changed, 152 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05ba4ca806..49081d90ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1137,7 +1137,7 @@ dependencies = [ "ckb-vm", "ckb-vm-definitions", "faster-hex 0.4.1", - "goblin", + "goblin 0.2.3", "proptest", "serde", "tiny-keccak", @@ -1491,26 +1491,39 @@ dependencies = [ [[package]] name = "ckb-vm" +<<<<<<< HEAD version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4b7a3a2f19771d379884030417094c55986ccbc0d5caaaa651f7421ead4bb4" +======= +version = "0.19.1" +source = "git+https://github.com/mohanson/ckb-vm?branch=instruction_design#fbd9af94342da2fe42839f965dbab1851aa87fb4" +>>>>>>> Bump ckb-vm dependencies = [ "byteorder", "bytes 1.0.1", "cc", "ckb-vm-definitions", "derive_more", - "goblin", + "goblin 0.2.3", + "goblin 0.3.4", "libc", "mapr", + "rand 0.7.3", "scroll", + "serde", ] [[package]] name = "ckb-vm-definitions" +<<<<<<< HEAD version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5dd4b68248ffd8283d693a477f9594d195ceac25f6aa7f98e99d1b8f43f9b3e" +======= +version = "0.19.1" +source = "git+https://github.com/mohanson/ckb-vm?branch=instruction_design#fbd9af94342da2fe42839f965dbab1851aa87fb4" +>>>>>>> Bump ckb-vm [[package]] name = "clap" @@ -2309,6 +2322,17 @@ dependencies = [ "scroll", ] +[[package]] +name = "goblin" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669cdc3826f69a51d3f8fc3f86de81c2378110254f678b8407977736122057a4" +dependencies = [ + "log", + "plain", + "scroll", +] + [[package]] name = "governor" version = "0.3.1" diff --git a/script/Cargo.toml b/script/Cargo.toml index a3a9050947..7f0f74d39a 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -21,14 +21,14 @@ ckb-traits = { path = "../traits", version = "= 0.43.0-pre" } byteorder = "1.3.1" ckb-types = {path = "../util/types", version = "= 0.43.0-pre"} ckb-hash = {path = "../util/hash", version = "= 0.43.0-pre"} -ckb-vm = { version = "0.19.4", default-features = false } +ckb-vm-definitions = { git = "https://github.com/mohanson/ckb-vm", branch="instruction_design" } +ckb-vm = { git = "https://github.com/mohanson/ckb-vm", branch="instruction_design", default-features = false } faster-hex = "0.4" ckb-logger = { path = "../util/logger", version = "= 0.43.0-pre", optional = true } serde = { version = "1.0", features = ["derive"] } ckb-error = { path = "../error", version = "= 0.43.0-pre" } ckb-chain-spec = { path = "../spec", version = "= 0.43.0-pre" } goblin = "0.2" -ckb-vm-definitions = "0.19.4" [dev-dependencies] proptest = "0.9" diff --git a/script/src/cost_model.rs b/script/src/cost_model.rs index dbcd458aa1..dce2c33adc 100644 --- a/script/src/cost_model.rs +++ b/script/src/cost_model.rs @@ -10,6 +10,9 @@ use ckb_vm::{ // 0.25 cycles per byte pub const BYTES_PER_CYCLE: u64 = 4; +/// The cost of switching between assembly and rust. +pub const CONTEXT_SWITCH_CYCLE: u64 = 500; + /// Calculates how many cycles spent to load the specified number of bytes. pub fn transferred_byte_cycles(bytes: u64) -> u64 { // Compiler will optimize the divisin here to shifts. @@ -19,6 +22,7 @@ pub fn transferred_byte_cycles(bytes: u64) -> u64 { /// Returns the spent cycles to execute the secific instruction. pub fn instruction_cycles(i: Instruction) -> u64 { match extract_opcode(i) { + // IMC insts::OP_JALR => 3, insts::OP_LD => 2, insts::OP_LW => 3, @@ -37,24 +41,9 @@ pub fn instruction_cycles(i: Instruction) -> u64 { insts::OP_BLT => 3, insts::OP_BLTU => 3, insts::OP_BNE => 3, - insts::OP_EBREAK => 500, - insts::OP_ECALL => 500, + insts::OP_EBREAK => CONTEXT_SWITCH_CYCLE, + insts::OP_ECALL => CONTEXT_SWITCH_CYCLE, insts::OP_JAL => 3, - insts::OP_RVC_LW => 3, - insts::OP_RVC_LD => 2, - insts::OP_RVC_SW => 3, - insts::OP_RVC_SD => 2, - insts::OP_RVC_LWSP => 3, - insts::OP_RVC_LDSP => 2, - insts::OP_RVC_SWSP => 3, - insts::OP_RVC_SDSP => 2, - insts::OP_RVC_BEQZ => 3, - insts::OP_RVC_BNEZ => 3, - insts::OP_RVC_JAL => 3, - insts::OP_RVC_J => 3, - insts::OP_RVC_JR => 3, - insts::OP_RVC_JALR => 3, - insts::OP_RVC_EBREAK => 500, insts::OP_MUL => 5, insts::OP_MULW => 5, insts::OP_MULH => 5, @@ -68,6 +57,51 @@ pub fn instruction_cycles(i: Instruction) -> u64 { insts::OP_REMW => 32, insts::OP_REMU => 32, insts::OP_REMUW => 32, + // B + insts::OP_GREV => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_GREVI => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_GREVW => CONTEXT_SWITCH_CYCLE + 18, + insts::OP_GREVIW => CONTEXT_SWITCH_CYCLE + 18, + insts::OP_SHFL => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_UNSHFL => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_SHFLI => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_UNSHFLI => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_SHFLW => CONTEXT_SWITCH_CYCLE + 18, + insts::OP_UNSHFLW => CONTEXT_SWITCH_CYCLE + 18, + insts::OP_GORC => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_GORCI => CONTEXT_SWITCH_CYCLE + 20, + insts::OP_GORCW => CONTEXT_SWITCH_CYCLE + 18, + insts::OP_GORCIW => CONTEXT_SWITCH_CYCLE + 18, + insts::OP_BFP => CONTEXT_SWITCH_CYCLE + 15, + insts::OP_BFPW => CONTEXT_SWITCH_CYCLE + 15, + insts::OP_BDEP => CONTEXT_SWITCH_CYCLE + 350, + insts::OP_BEXT => CONTEXT_SWITCH_CYCLE + 270, + insts::OP_BDEPW => CONTEXT_SWITCH_CYCLE + 180, + insts::OP_BEXTW => CONTEXT_SWITCH_CYCLE + 140, + insts::OP_CLMUL => CONTEXT_SWITCH_CYCLE + 320, + insts::OP_CLMULR => CONTEXT_SWITCH_CYCLE + 380, + insts::OP_CLMULH => CONTEXT_SWITCH_CYCLE + 400, + insts::OP_CLMULW => CONTEXT_SWITCH_CYCLE + 60, + insts::OP_CLMULRW => CONTEXT_SWITCH_CYCLE + 60, + insts::OP_CLMULHW => CONTEXT_SWITCH_CYCLE + 60, + insts::OP_CRC32B => CONTEXT_SWITCH_CYCLE + 15, + insts::OP_CRC32H => CONTEXT_SWITCH_CYCLE + 30, + insts::OP_CRC32W => CONTEXT_SWITCH_CYCLE + 45, + insts::OP_CRC32D => CONTEXT_SWITCH_CYCLE + 60, + insts::OP_CRC32CB => CONTEXT_SWITCH_CYCLE + 15, + insts::OP_CRC32CH => CONTEXT_SWITCH_CYCLE + 30, + insts::OP_CRC32CW => CONTEXT_SWITCH_CYCLE + 45, + insts::OP_CRC32CD => CONTEXT_SWITCH_CYCLE + 60, + insts::OP_BMATFLIP => CONTEXT_SWITCH_CYCLE + 40, + insts::OP_BMATOR => CONTEXT_SWITCH_CYCLE + 500, + insts::OP_BMATXOR => CONTEXT_SWITCH_CYCLE + 800, + // MOP + insts::OP_WIDE_MUL => 5, + insts::OP_WIDE_MULU => 5, + insts::OP_WIDE_DIV => 32, + insts::OP_WIDE_DIVU => 32, + insts::OP_FAR_JUMP_REL => 3, + insts::OP_FAR_JUMP_ABS => 3, _ => 1, } } diff --git a/script/src/ill_transaction_checker.rs b/script/src/ill_transaction_checker.rs index bcfceecf6b..b5e0916c7e 100644 --- a/script/src/ill_transaction_checker.rs +++ b/script/src/ill_transaction_checker.rs @@ -2,8 +2,8 @@ use crate::ScriptError; use byteorder::{ByteOrder, LittleEndian}; use ckb_types::core::TransactionView; use ckb_vm::{ - instructions::{extract_opcode, i, m, rvc, Instruction, Itype, Stype}, - registers::{RA, ZERO}, + instructions::{b, extract_opcode, i, m, rvc, Instruction, Itype}, + registers::ZERO, }; use ckb_vm_definitions::instructions as insts; use goblin::elf::{section_header::SHF_EXECINSTR, Elf}; @@ -56,35 +56,19 @@ impl<'a> IllScriptChecker<'a> { let mut pc = section_header.sh_offset; let end = section_header.sh_offset + section_header.sh_size; while pc < end { - match self.decode_instruction(pc) { - (Some(i), len) => { - match extract_opcode(i) { - insts::OP_JALR => { - let i = Itype(i); - if i.rs1() == i.rd() && i.rd() != ZERO { - return Err(ScriptError::EncounteredKnownBugs( - CKB_VM_ISSUE_92.to_string(), - self.index, - )); - } - } - insts::OP_RVC_JALR => { - let i = Stype(i); - if i.rs1() == RA { - return Err(ScriptError::EncounteredKnownBugs( - CKB_VM_ISSUE_92.to_string(), - self.index, - )); - } - } - _ => (), - }; - pc += len; - } - (None, len) => { - pc += len; - } + let (option_instruction, len) = self.decode_instruction(pc); + if let Some(i) = option_instruction { + if extract_opcode(i) == insts::OP_JALR { + let i = Itype(i); + if i.rs1() == i.rd() && i.rd() != ZERO { + return Err(ScriptError::EncounteredKnownBugs( + CKB_VM_ISSUE_92.to_string(), + self.index, + )); + } + }; } + pc += len; } } } @@ -103,7 +87,12 @@ impl<'a> IllScriptChecker<'a> { } i = LittleEndian::read_u32(&self.data[pc as usize..]); } - let factories = [rvc::factory::, i::factory::, m::factory::]; + let factories = [ + rvc::factory::, + i::factory::, + m::factory::, + b::factory::, + ]; for factory in &factories { if let Some(instruction) = factory(i) { return (Some(instruction), len); diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 1382cb75fd..7a305ee0c7 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -193,9 +193,11 @@ mod tests { }; use ckb_vm::machine::DefaultCoreMachine; use ckb_vm::{ - memory::{FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE}, + machine::VERSION0, + memory::{FLAG_DIRTY, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE}, registers::{A0, A1, A2, A3, A4, A5, A7}, - CoreMachine, Error as VMError, Memory, SparseMemory, Syscalls, WXorXMemory, RISCV_PAGESIZE, + CoreMachine, Error as VMError, Memory, SparseMemory, Syscalls, WXorXMemory, ISA_IMC, + RISCV_PAGESIZE, }; use proptest::{collection::size_range, prelude::*}; use std::collections::HashMap; @@ -1066,7 +1068,12 @@ mod tests { } fn _test_load_cell_data_as_code(data: &[u8]) -> Result<(), TestCaseError> { - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); + let addr = 4096; let addr_size = 4096; @@ -1102,7 +1109,7 @@ mod tests { prop_assert!(load_code.ecall(&mut machine).is_ok()); prop_assert_eq!(machine.registers()[A0], u64::from(SUCCESS)); - let flags = FLAG_EXECUTABLE | FLAG_FREEZED; + let flags = FLAG_EXECUTABLE | FLAG_FREEZED | FLAG_DIRTY; prop_assert_eq!( machine .memory_mut() @@ -1121,7 +1128,11 @@ mod tests { } fn _test_load_cell_data(data: &[u8]) -> Result<(), TestCaseError> { - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); let size_addr: u64 = 100; let addr = 4096; let addr_size = 4096; @@ -1157,7 +1168,7 @@ mod tests { prop_assert!(load_code.ecall(&mut machine).is_ok()); prop_assert_eq!(machine.registers()[A0], u64::from(SUCCESS)); - let flags = FLAG_WRITABLE; + let flags = FLAG_WRITABLE | FLAG_DIRTY; prop_assert_eq!( machine .memory_mut() @@ -1193,7 +1204,11 @@ mod tests { #[test] fn test_load_overflowed_cell_data_as_code() { let data = vec![0, 1, 2, 3, 4, 5]; - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); let addr = 4096; let addr_size = 4096; @@ -1234,7 +1249,11 @@ mod tests { as_code: bool, data: &[u8], ) -> Result<(), TestCaseError> { - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); let addr = 8192; let addr_size = 4096; @@ -1300,7 +1319,11 @@ mod tests { #[test] fn test_load_code_unaligned_error() { - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); let addr = 4097; let addr_size = 4096; let data = [2; 32]; @@ -1342,7 +1365,11 @@ mod tests { #[test] fn test_load_code_slice_out_of_bound_error() { - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); let addr = 4096; let addr_size = 4096; let data = [2; 32]; @@ -1386,7 +1413,11 @@ mod tests { #[test] fn test_load_code_not_enough_space_error() { - let mut machine = DefaultCoreMachine::>>::default(); + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); let addr = 4096; let addr_size = 4096; diff --git a/script/src/verify.rs b/script/src/verify.rs index 7309f79bb6..3d793d045e 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -24,9 +24,12 @@ use ckb_types::{ }; #[cfg(has_asm)] use ckb_vm::{ - machine::asm::{AsmCoreMachine, AsmMachine}, + machine::{ + asm::{AsmCoreMachine, AsmMachine}, + VERSION0, + }, DefaultMachineBuilder, Error as VMInternalError, InstructionCycleFunc, SupportMachine, - Syscalls, + Syscalls, ISA_B, ISA_IMC, }; #[cfg(not(has_asm))] use ckb_vm::{ @@ -406,12 +409,13 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D fn run(&self, script_group: &ScriptGroup, max_cycles: Cycle) -> Result { let program = self.extract_script(&script_group.script)?; #[cfg(has_asm)] - let core_machine = AsmCoreMachine::new_with_max_cycles(max_cycles); + let core_machine = AsmCoreMachine::new(ISA_IMC | ISA_B, VERSION0, max_cycles); #[cfg(not(has_asm))] - let core_machine = - DefaultCoreMachine::>>::new_with_max_cycles( - max_cycles, - ); + let core_machine = DefaultCoreMachine::>>::new( + ISA_IMC | ISA_B, + VERSION0, + max_cycles, + ); let machine_builder = DefaultMachineBuilder::::new(core_machine) .instruction_cycle_func(self.cost_model()); let machine_builder = self From ca6d22af7489ba3d049bbb537c56e433e3001224 Mon Sep 17 00:00:00 2001 From: mohanson Date: Thu, 20 May 2021 09:39:13 +0800 Subject: [PATCH 02/14] feat: new syscalls --- Cargo.lock | 22 +--- script/Cargo.toml | 4 +- script/src/syscalls/current_cycles.rs | 28 +++++ script/src/syscalls/exec.rs | 156 ++++++++++++++++++++++++++ script/src/syscalls/mod.rs | 83 +++++++++++++- script/src/syscalls/vm_version.rs | 28 +++++ script/src/verify.rs | 135 +++++++++++++++++++++- script/testdata/current_cycles | Bin 0 -> 6400 bytes script/testdata/current_cycles.c | 23 ++++ script/testdata/exec_callee | Bin 0 -> 6352 bytes script/testdata/exec_callee.c | 21 ++++ script/testdata/exec_caller | Bin 0 -> 6552 bytes script/testdata/exec_caller.c | 28 +++++ script/testdata/vm_version | Bin 0 -> 6400 bytes script/testdata/vm_version.c | 23 ++++ 15 files changed, 528 insertions(+), 23 deletions(-) create mode 100644 script/src/syscalls/current_cycles.rs create mode 100644 script/src/syscalls/exec.rs create mode 100644 script/src/syscalls/vm_version.rs create mode 100755 script/testdata/current_cycles create mode 100644 script/testdata/current_cycles.c create mode 100755 script/testdata/exec_callee create mode 100644 script/testdata/exec_callee.c create mode 100755 script/testdata/exec_caller create mode 100644 script/testdata/exec_caller.c create mode 100755 script/testdata/vm_version create mode 100644 script/testdata/vm_version.c diff --git a/Cargo.lock b/Cargo.lock index 49081d90ad..50b036fc7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1491,14 +1491,8 @@ dependencies = [ [[package]] name = "ckb-vm" -<<<<<<< HEAD -version = "0.19.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4b7a3a2f19771d379884030417094c55986ccbc0d5caaaa651f7421ead4bb4" -======= version = "0.19.1" -source = "git+https://github.com/mohanson/ckb-vm?branch=instruction_design#fbd9af94342da2fe42839f965dbab1851aa87fb4" ->>>>>>> Bump ckb-vm +source = "git+https://github.com/mohanson/ckb-vm?branch=develop#09e1e21995acaea3f75d26e144e652b4c4ba67ea" dependencies = [ "byteorder", "bytes 1.0.1", @@ -1506,7 +1500,7 @@ dependencies = [ "ckb-vm-definitions", "derive_more", "goblin 0.2.3", - "goblin 0.3.4", + "goblin 0.4.0", "libc", "mapr", "rand 0.7.3", @@ -1516,14 +1510,8 @@ dependencies = [ [[package]] name = "ckb-vm-definitions" -<<<<<<< HEAD -version = "0.19.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5dd4b68248ffd8283d693a477f9594d195ceac25f6aa7f98e99d1b8f43f9b3e" -======= version = "0.19.1" -source = "git+https://github.com/mohanson/ckb-vm?branch=instruction_design#fbd9af94342da2fe42839f965dbab1851aa87fb4" ->>>>>>> Bump ckb-vm +source = "git+https://github.com/mohanson/ckb-vm?branch=develop#09e1e21995acaea3f75d26e144e652b4c4ba67ea" [[package]] name = "clap" @@ -2324,9 +2312,9 @@ dependencies = [ [[package]] name = "goblin" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "669cdc3826f69a51d3f8fc3f86de81c2378110254f678b8407977736122057a4" +checksum = "532a09cd3df2c6bbfc795fb0434bff8f22255d1d07328180e918a2e6ce122d4d" dependencies = [ "log", "plain", diff --git a/script/Cargo.toml b/script/Cargo.toml index 7f0f74d39a..6f788993f0 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -21,8 +21,8 @@ ckb-traits = { path = "../traits", version = "= 0.43.0-pre" } byteorder = "1.3.1" ckb-types = {path = "../util/types", version = "= 0.43.0-pre"} ckb-hash = {path = "../util/hash", version = "= 0.43.0-pre"} -ckb-vm-definitions = { git = "https://github.com/mohanson/ckb-vm", branch="instruction_design" } -ckb-vm = { git = "https://github.com/mohanson/ckb-vm", branch="instruction_design", default-features = false } +ckb-vm-definitions = { git = "https://github.com/mohanson/ckb-vm", branch="develop" } +ckb-vm = { git = "https://github.com/mohanson/ckb-vm", branch="develop", default-features = false } faster-hex = "0.4" ckb-logger = { path = "../util/logger", version = "= 0.43.0-pre", optional = true } serde = { version = "1.0", features = ["derive"] } diff --git a/script/src/syscalls/current_cycles.rs b/script/src/syscalls/current_cycles.rs new file mode 100644 index 0000000000..4f80c73d3f --- /dev/null +++ b/script/src/syscalls/current_cycles.rs @@ -0,0 +1,28 @@ +use crate::syscalls::CURRENT_CYCLES; +use ckb_vm::{ + registers::{A0, A7}, + Error as VMError, Register, SupportMachine, Syscalls, +}; + +#[derive(Debug)] +pub struct CurrentCycles {} + +impl CurrentCycles { + pub fn new() -> Self { + Self {} + } +} + +impl Syscalls for CurrentCycles { + fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { + Ok(()) + } + + fn ecall(&mut self, machine: &mut Mac) -> Result { + if machine.registers()[A7].to_u64() != CURRENT_CYCLES { + return Ok(false); + } + machine.set_register(A0, Mac::REG::from_u64(machine.cycles())); + Ok(true) + } +} diff --git a/script/src/syscalls/exec.rs b/script/src/syscalls/exec.rs new file mode 100644 index 0000000000..e682348434 --- /dev/null +++ b/script/src/syscalls/exec.rs @@ -0,0 +1,156 @@ +use crate::cost_model::transferred_byte_cycles; +use crate::syscalls::{Source, SourceEntry, EXEC, INDEX_OUT_OF_BOUND, WRONG_FORMAT}; +use ckb_traits::CellDataProvider; +use ckb_types::core::cell::CellMeta; +use ckb_vm::Memory; +use ckb_vm::{ + registers::{A0, A1, A2, A3, A7}, + Bytes, Error as VMError, Register, SupportMachine, Syscalls, +}; +use ckb_vm::{DEFAULT_STACK_SIZE, RISCV_MAX_MEMORY}; + +#[derive(Debug)] +pub struct Exec<'a, DL> { + data_loader: &'a DL, + outputs: &'a [CellMeta], + resolved_inputs: &'a [CellMeta], + resolved_cell_deps: &'a [CellMeta], + group_inputs: &'a [usize], + group_outputs: &'a [usize], +} + +impl<'a, DL: CellDataProvider + 'a> Exec<'a, DL> { + pub fn new( + data_loader: &'a DL, + outputs: &'a [CellMeta], + resolved_inputs: &'a [CellMeta], + resolved_cell_deps: &'a [CellMeta], + group_inputs: &'a [usize], + group_outputs: &'a [usize], + ) -> Exec<'a, DL> { + Exec { + data_loader, + outputs, + resolved_inputs, + resolved_cell_deps, + group_inputs, + group_outputs, + } + } + + fn fetch_cell(&self, source: Source, index: usize) -> Result<&'a 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) + } + 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), + } + } +} + +fn load_c_string(machine: &mut Mac, addr: u64) -> Result { + let mut buffer = Vec::new(); + let mut addr = addr; + + loop { + let byte = machine + .memory_mut() + .load8(&Mac::REG::from_u64(addr))? + .to_u8(); + if byte == 0 { + break; + } + buffer.push(byte); + addr += 1; + } + + Ok(Bytes::from(buffer)) +} + +impl<'a, Mac: SupportMachine, DL: CellDataProvider> Syscalls for Exec<'a, DL> { + fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { + Ok(()) + } + + fn ecall(&mut self, machine: &mut Mac) -> Result { + if machine.registers()[A7].to_u64() != EXEC { + return Ok(false); + } + + let index = machine.registers()[A0].to_u64(); + let source = Source::parse_from_u64(machine.registers()[A1].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(false); + } + let cell = cell.unwrap(); + let data = self + .data_loader + .load_cell_data(cell) + .ok_or(VMError::Unexpected)?; + + let argc = machine.registers()[A2].to_u64(); + let mut addr = machine.registers()[A3].to_u64(); + let mut argv = Vec::new(); + for _ in 0..argc { + let target_addr = machine + .memory_mut() + .load64(&Mac::REG::from_u64(addr))? + .to_u64(); + + let cstr = load_c_string(machine, target_addr)?; + argv.push(cstr); + addr += 8; + } + + machine.reset(); + + match machine.load_elf(&data, true) { + Ok(size) => { + machine.add_cycles(transferred_byte_cycles(size))?; + } + Err(_) => { + machine.set_register(A0, Mac::REG::from_u8(WRONG_FORMAT)); + return Ok(false); + } + } + + match machine.initialize_stack( + &argv, + (RISCV_MAX_MEMORY - DEFAULT_STACK_SIZE) as u64, + DEFAULT_STACK_SIZE as u64, + ) { + Ok(size) => { + machine.add_cycles(transferred_byte_cycles(size))?; + } + Err(_) => { + machine.set_register(A0, Mac::REG::from_u8(WRONG_FORMAT)); + return Ok(false); + } + } + Ok(true) + } +} diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 7a305ee0c7..92fd900f32 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -1,4 +1,6 @@ +mod current_cycles; mod debugger; +mod exec; mod load_cell; mod load_cell_data; mod load_header; @@ -8,8 +10,11 @@ mod load_script_hash; mod load_tx; mod load_witness; mod utils; +mod vm_version; +pub use self::current_cycles::CurrentCycles; pub use self::debugger::Debugger; +pub use self::exec::Exec; pub use self::load_cell::LoadCell; pub use self::load_cell_data::LoadCellData; pub use self::load_header::LoadHeader; @@ -18,6 +23,7 @@ pub use self::load_script::LoadScript; pub use self::load_script_hash::LoadScriptHash; pub use self::load_tx::LoadTx; pub use self::load_witness::LoadWitness; +pub use self::vm_version::VMVersion; use ckb_vm::Error; @@ -29,7 +35,11 @@ pub const SUCCESS: u8 = 0; pub const INDEX_OUT_OF_BOUND: u8 = 1; pub const ITEM_MISSING: u8 = 2; pub const SLICE_OUT_OF_BOUND: u8 = 3; +pub const WRONG_FORMAT: u8 = 4; +pub const VM_VERSION: u64 = 2041; +pub const CURRENT_CYCLES: u64 = 2042; +pub const EXEC: u64 = 2043; pub const LOAD_TRANSACTION_SYSCALL_NUMBER: u64 = 2051; pub const LOAD_SCRIPT_SYSCALL_NUMBER: u64 = 2052; pub const LOAD_TX_HASH_SYSCALL_NUMBER: u64 = 2061; @@ -191,9 +201,9 @@ mod tests { prelude::*, H256, }; - use ckb_vm::machine::DefaultCoreMachine; + use ckb_vm::{SupportMachine, machine::DefaultCoreMachine}; use ckb_vm::{ - machine::VERSION0, + machine::{VERSION0, VERSION1}, memory::{FLAG_DIRTY, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE}, registers::{A0, A1, A2, A3, A4, A5, A7}, CoreMachine, Error as VMError, Memory, SparseMemory, Syscalls, WXorXMemory, ISA_IMC, @@ -1460,4 +1470,73 @@ mod tests { assert_eq!(machine.memory_mut().load8(&i), Ok(1)); } } + + #[test] + fn test_vm_version0() { + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION0, + u64::max_value(), + ); + + machine.set_register(A0, 0); + machine.set_register(A1, 0); + machine.set_register(A2, 0); + machine.set_register(A3, 0); + machine.set_register(A4, 0); + machine.set_register(A5, 0); + machine.set_register(A7, VM_VERSION); + + let result = VMVersion::new().ecall(&mut machine); + + assert_eq!(result.unwrap(), true); + assert_eq!(machine.registers()[A0], 0); + } + + #[test] + fn test_vm_version1() { + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION1, + u64::max_value(), + ); + + machine.set_register(A0, 0); + machine.set_register(A1, 0); + machine.set_register(A2, 0); + machine.set_register(A3, 0); + machine.set_register(A4, 0); + machine.set_register(A5, 0); + machine.set_register(A7, VM_VERSION); + + let result = VMVersion::new().ecall(&mut machine); + + assert_eq!(result.unwrap(), true); + assert_eq!(machine.registers()[A0], 1); + } + + #[test] + fn test_current_cycles() { + let mut machine = DefaultCoreMachine::>>::new( + ISA_IMC, + VERSION1, + u64::max_value(), + ); + + machine.set_register(A0, 0); + machine.set_register(A1, 0); + machine.set_register(A2, 0); + machine.set_register(A3, 0); + machine.set_register(A4, 0); + machine.set_register(A5, 0); + machine.set_register(A7, CURRENT_CYCLES); + + machine.set_cycles(100); + + let result = CurrentCycles::new().ecall(&mut machine); + + assert_eq!(result.unwrap(), true); + assert_eq!(machine.registers()[A0], 100); + } + } diff --git a/script/src/syscalls/vm_version.rs b/script/src/syscalls/vm_version.rs new file mode 100644 index 0000000000..ad2283dfcb --- /dev/null +++ b/script/src/syscalls/vm_version.rs @@ -0,0 +1,28 @@ +use crate::syscalls::VM_VERSION; +use ckb_vm::{ + registers::{A0, A7}, + Error as VMError, Register, SupportMachine, Syscalls, +}; + +#[derive(Debug)] +pub struct VMVersion {} + +impl VMVersion { + pub fn new() -> Self { + Self {} + } +} + +impl Syscalls for VMVersion { + fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> { + Ok(()) + } + + fn ecall(&mut self, machine: &mut Mac) -> Result { + if machine.registers()[A7].to_u64() != VM_VERSION { + return Ok(false); + } + machine.set_register(A0, Mac::REG::from_u32(machine.version())); + Ok(true) + } +} diff --git a/script/src/verify.rs b/script/src/verify.rs index 3d793d045e..26f6800453 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -2,8 +2,8 @@ use crate::{ cost_model::{instruction_cycles, transferred_byte_cycles}, error::ScriptError, syscalls::{ - Debugger, LoadCell, LoadCellData, LoadHeader, LoadInput, LoadScript, LoadScriptHash, - LoadTx, LoadWitness, + CurrentCycles, Debugger, Exec, LoadCell, LoadCellData, LoadHeader, LoadInput, LoadScript, + LoadScriptHash, LoadTx, LoadWitness, VMVersion, }, type_id::TypeIdSystemScript, types::{ScriptGroup, ScriptGroupType}, @@ -200,6 +200,25 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D self.rtx.transaction.hash() } + fn build_current_cycles(&self) -> CurrentCycles { + CurrentCycles::new() + } + + fn build_vm_version(&self) -> VMVersion { + VMVersion::new() + } + + fn build_exec(&'a self, group_inputs: &'a [usize], group_outputs: &'a [usize]) -> Exec<'a, DL> { + Exec::new( + &self.data_loader, + &self.outputs, + self.resolved_inputs(), + self.resolved_cell_deps(), + group_inputs, + group_outputs, + ) + } + fn build_load_tx(&self) -> LoadTx { LoadTx::new(&self.rtx.transaction) } @@ -385,6 +404,9 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D ) -> Vec + 'a)>> { let current_script_hash = script_group.script.calc_script_hash(); vec![ + Box::new(self.build_vm_version()), + Box::new(self.build_current_cycles()), + Box::new(self.build_exec(&script_group.input_indices, &script_group.output_indices)), Box::new(self.build_load_script_hash(current_script_hash.clone())), Box::new(self.build_load_tx()), Box::new( @@ -1797,4 +1819,113 @@ mod tests { assert!(cycle <= TWO_IN_TWO_OUT_CYCLES); assert!(cycle >= TWO_IN_TWO_OUT_CYCLES - CYCLE_BOUND); } + + #[test] + fn check_vm_version() { + let vm_version_cell_data = Bytes::from(std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/vm_version")).unwrap()); + let vm_version_cell = CellOutput::new_builder() + .capacity( + Capacity::bytes(vm_version_cell_data.len()) + .unwrap() + .pack(), + ) + .build(); + let vm_version_script = Script::new_builder().hash_type(ScriptHashType::Data.into()) + .code_hash(CellOutput::calc_data_hash(&vm_version_cell_data)) + .build(); + let output = CellOutputBuilder::default() + .capacity(capacity_bytes!(100).pack()) + .lock(vm_version_script.clone()) + .build(); + let input = CellInput::new(OutPoint::null(), 0); + + let transaction = TransactionBuilder::default().input(input).build(); + + let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) + .transaction_info(default_transaction_info()) + .build(); + let vm_version_cell = CellMetaBuilder::from_cell_output( + vm_version_cell.clone(), + vm_version_cell_data.to_owned(), + ) + .transaction_info(default_transaction_info()) + .build(); + + let rtx = ResolvedTransaction { + transaction, + resolved_cell_deps: vec![vm_version_cell], + resolved_inputs: vec![dummy_cell], + resolved_dep_groups: vec![], + }; + + let store = new_store(); + let data_loader = DataLoaderWrapper::new(&store); + + let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); + assert!(verifier.verify(6000).is_ok()); + } + + #[test] + fn check_exec() { + let exec_caller_cell_data = Bytes::from(std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_caller")).unwrap()); + let exec_caller_cell = CellOutput::new_builder() + .capacity( + Capacity::bytes(exec_caller_cell_data.len()) + .unwrap() + .pack(), + ) + .build(); + + let exec_callee_cell_data = Bytes::from(std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_callee")).unwrap()); + let exec_callee_cell = CellOutput::new_builder() + .capacity( + Capacity::bytes(exec_callee_cell_data.len()) + .unwrap() + .pack(), + ) + .build(); + + let exec_caller_script = Script::new_builder().hash_type(ScriptHashType::Data.into()) + .code_hash(CellOutput::calc_data_hash(&exec_caller_cell_data)) + .build(); + let output = CellOutputBuilder::default() + .capacity(capacity_bytes!(100).pack()) + .lock(exec_caller_script.clone()) + .build(); + let input = CellInput::new(OutPoint::null(), 0); + + let transaction = TransactionBuilder::default().input(input).build(); + + let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) + .transaction_info(default_transaction_info()) + .build(); + let exec_caller_cell = CellMetaBuilder::from_cell_output( + exec_caller_cell.clone(), + exec_caller_cell_data.to_owned(), + ) + .transaction_info(default_transaction_info()) + .build(); + + let exec_callee_cell = CellMetaBuilder::from_cell_output( + exec_callee_cell.clone(), + exec_callee_cell_data.to_owned(), + ) + .transaction_info(default_transaction_info()) + .build(); + + let rtx = ResolvedTransaction { + transaction, + resolved_cell_deps: vec![exec_caller_cell, exec_callee_cell], + resolved_inputs: vec![dummy_cell], + resolved_dep_groups: vec![], + }; + + let store = new_store(); + let data_loader = DataLoaderWrapper::new(&store); + + let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); + println!("{:?}", verifier.verify(600000)); + assert!(verifier.verify(600000).is_ok()); + } + } diff --git a/script/testdata/current_cycles b/script/testdata/current_cycles new file mode 100755 index 0000000000000000000000000000000000000000..dc53642971e1cba3a675e224fcef5cbf90a68082 GIT binary patch literal 6400 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 literal 0 HcmV?d00001 diff --git a/script/testdata/current_cycles.c b/script/testdata/current_cycles.c new file mode 100644 index 0000000000..d7e9dcf046 --- /dev/null +++ b/script/testdata/current_cycles.c @@ -0,0 +1,23 @@ +static inline long __internal_syscall(long n, long _a0, long _a1, long _a2, + long _a3, long _a4, long _a5) { + register long a0 asm("a0") = _a0; + register long a1 asm("a1") = _a1; + register long a2 asm("a2") = _a2; + register long a3 asm("a3") = _a3; + register long a4 asm("a4") = _a4; + register long a5 asm("a5") = _a5; + register long syscall_id asm("a7") = n; + + asm volatile("scall" + : "+r"(a0) + : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(syscall_id)); + return a0; +} + +#define syscall(n, a, b, c, d, e, f) \ + __internal_syscall(n, (long)(a), (long)(b), (long)(c), (long)(d), (long)(e), \ + (long)(f)) + +int main() { + return syscall(2042, 0, 0, 0, 0, 0, 0); +} diff --git a/script/testdata/exec_callee b/script/testdata/exec_callee new file mode 100755 index 0000000000000000000000000000000000000000..5d08c24b6a57431c87e7bf94f11f364cd98b13b6 GIT binary patch literal 6352 zcmeHMeN0=|6+iF!3q;leP8!yb;=~=Kq_SWkg{&#V5aT3j8O=wvbZZ~x`30O}OzcMj z+oJgcheAR!8RFigfT?6XArLC?|j^I&i#1z**|r>T+MSFu)@O^Kzdh{CqM;d{A`WdAiN4F10J8> zfK`f=fy%@sNK3WJQzoO38J99*Q&0K&HL5(LKQXCtmnda=!`P2?n&9k2seuEt;TLuv zTrLX-`!^sS3XPki++FkW(EapNA;%oltzV1w&4ybqd~;lF8Me&9<*jpIS;IS?2d)}h z89u1XHg|^RV_3x#(@?5I9i0wDgAUBm&^#Xr2V+PH|J|ORXqWTi_yXVir(|-v3GVKi z&+S^ZM3OnW? zn63LTG{4>3I~MNW7{!ckLa%0aUw612o|vhl)|}o|yCwa8g>g22%6wp3g_w~JW_?F( zXh&{cRKGzV?w`y(yO<#TDv?etd9$w4J>cd&#E+M%3OmKaV|?uBJs6$*-Hq50Wa@DlmH0k~OV%E)Y@S<_7qTATd1N8MZ1kZ!jGz^2rVX$HyG$6$~s7H!*P=^%j zAdeL5Acqv|ARxtxv%j$LWUMa=*Lq@o22OeM*Ln^N$NCCD9piX-I+*`eUvPBo`qSlo zlZVH?e0TVb!}Zs7zYWdT%XWS1<#c-$IdPxtiPa_5C#UDS_4Ux@Vuy8t5aQ1V)pGZxfJ$gSLiX$I(WGWKVuSNO(J0n)~V|3Vt?a>i3qvH(@H_qFuxa0POPUlYYuP_6UDYC7<737GIAt^xoSFD_|H(Vp-yegQ_gbGU{V!q_C`1A1O{)3dNP;Y=j_??0`cPigB^`Vpms_jM?0 zQ~*zG`Rn^7+1}4)|7C!TuR~yuV|bi_#~FB>fyWv6zh=Nf@Au3J)^}D_eaEuNS+oB+ zOPOtptqAO!P{i*Eh(~~X2Ac-|krzrl-)<=qyyW2`4-p^C(0`#ie5bSbdZL&Pzh2mP z#&TQ-X&OHXUk7@~Ns1frJA%DABzY)AoY+az+<%^@_$CehU5ab(OZyf3@pxG_+zP%jpC_mtcK^bz zQpnd22PSx%=8et!pn~flQ)f(l5g=|WWe~gB7}*28ij&_ENO@n8U%Br-Mm&pOiBC|xMYA72A$Y?6Wjk{kaWd{s zvXHPGQl0?!DgHF|r-b4e_~l9Df2dRCe@}5c#kIe${DTgV3mHI=@9i<3@=xS;bT(Z>Wk!>RQr3Ozxmi)p&PqPO^zZ{TT z4<1B@TUuhtUO{Z~HA?{y1h-FUX!6y&ngqA(^9KZ1Ya56@Z%dOTOK#iNlG38&LW+IC z<@dYV1*uu~x5Gid%PR@)Ri-r(R5Olp*o(5K&eF$w!OdCz8osnsEjqR=IcY(xCN$fS|hVmAnB- z2CvK04C-1!(dF(+%3LV<2CxO%y|Swwsq9y2BWvMGl))x7s?!d(c;dj8;I2(EXA7tV zJpmi8J1?#!uqpQ(n@g7cp88f9*XjRU)}aM*;;?g(-G4rymiOqF4}sflS=oR+qK`j; zDuvyXmVT$DI3pT!uo)RoU}gI3J&;C!=gnk_l(KV$(Ne^pOwecVogAvq=CX**&h29*pc>#pKzztY3=_g0cA7`5dJBWlWG#t-gA10w~hR zF@18+ODrT-QW$*`+cG;njV5G_P7)<)wH9>fnss0SjBl&HJ*kb;(ypS6! eiyfOw;F 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 q Date: Wed, 26 May 2021 21:29:11 +0800 Subject: [PATCH 03/14] redesign exec --- script/src/cost_model.rs | 1 + script/src/syscalls/exec.rs | 67 ++++++++--- script/src/syscalls/mod.rs | 3 +- script/src/verify.rs | 107 ++++++++++++++---- script/testdata/exec_callee.c | 6 - ...exec_caller => exec_caller_from_cell_data} | Bin 6552 -> 6552 bytes ..._caller.c => exec_caller_from_cell_data.c} | 4 +- script/testdata/exec_caller_from_witness | Bin 0 -> 6560 bytes script/testdata/exec_caller_from_witness.c | 26 +++++ 9 files changed, 165 insertions(+), 49 deletions(-) rename script/testdata/{exec_caller => exec_caller_from_cell_data} (76%) rename script/testdata/{exec_caller.c => exec_caller_from_cell_data.c} (94%) create mode 100755 script/testdata/exec_caller_from_witness create mode 100644 script/testdata/exec_caller_from_witness.c diff --git a/script/src/cost_model.rs b/script/src/cost_model.rs index dce2c33adc..6607496d29 100644 --- a/script/src/cost_model.rs +++ b/script/src/cost_model.rs @@ -98,6 +98,7 @@ pub fn instruction_cycles(i: Instruction) -> u64 { // MOP insts::OP_WIDE_MUL => 5, insts::OP_WIDE_MULU => 5, + insts::OP_WIDE_MULSU => 5, insts::OP_WIDE_DIV => 32, insts::OP_WIDE_DIVU => 32, insts::OP_FAR_JUMP_REL => 3, diff --git a/script/src/syscalls/exec.rs b/script/src/syscalls/exec.rs index e682348434..55cce65662 100644 --- a/script/src/syscalls/exec.rs +++ b/script/src/syscalls/exec.rs @@ -2,9 +2,10 @@ use crate::cost_model::transferred_byte_cycles; use crate::syscalls::{Source, SourceEntry, EXEC, INDEX_OUT_OF_BOUND, WRONG_FORMAT}; use ckb_traits::CellDataProvider; use ckb_types::core::cell::CellMeta; +use ckb_types::packed::{Bytes as PackedBytes, BytesVec}; use ckb_vm::Memory; use ckb_vm::{ - registers::{A0, A1, A2, A3, A7}, + registers::{A0, A1, A2, A3, A4, A5, A7}, Bytes, Error as VMError, Register, SupportMachine, Syscalls, }; use ckb_vm::{DEFAULT_STACK_SIZE, RISCV_MAX_MEMORY}; @@ -17,6 +18,7 @@ pub struct Exec<'a, DL> { resolved_cell_deps: &'a [CellMeta], group_inputs: &'a [usize], group_outputs: &'a [usize], + witnesses: BytesVec, } impl<'a, DL: CellDataProvider + 'a> Exec<'a, DL> { @@ -27,6 +29,7 @@ impl<'a, DL: CellDataProvider + 'a> Exec<'a, DL> { resolved_cell_deps: &'a [CellMeta], group_inputs: &'a [usize], group_outputs: &'a [usize], + witnesses: BytesVec, ) -> Exec<'a, DL> { Exec { data_loader, @@ -35,6 +38,7 @@ impl<'a, DL: CellDataProvider + 'a> Exec<'a, DL> { resolved_cell_deps, group_inputs, group_outputs, + witnesses, } } @@ -68,6 +72,22 @@ impl<'a, DL: CellDataProvider + 'a> Exec<'a, DL> { Source::Group(SourceEntry::HeaderDep) => Err(INDEX_OUT_OF_BOUND), } } + + fn fetch_witness(&self, source: Source, index: usize) -> Option { + match source { + Source::Group(SourceEntry::Input) => self + .group_inputs + .get(index) + .and_then(|actual_index| self.witnesses.get(*actual_index)), + Source::Group(SourceEntry::Output) => self + .group_outputs + .get(index) + .and_then(|actual_index| self.witnesses.get(*actual_index)), + Source::Transaction(SourceEntry::Input) => self.witnesses.get(index), + Source::Transaction(SourceEntry::Output) => self.witnesses.get(index), + _ => None, + } + } } fn load_c_string(machine: &mut Mac, addr: u64) -> Result { @@ -101,19 +121,40 @@ impl<'a, Mac: SupportMachine, DL: CellDataProvider> Syscalls for Exec<'a, D let index = machine.registers()[A0].to_u64(); let source = Source::parse_from_u64(machine.registers()[A1].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(false); - } - let cell = cell.unwrap(); - let data = self - .data_loader - .load_cell_data(cell) - .ok_or(VMError::Unexpected)?; + let place = machine.registers()[A2].to_u64(); + let bounds = machine.registers()[A3].to_u64(); + let offset = bounds as usize >> 32; + let length = bounds as u32 as usize; - let argc = machine.registers()[A2].to_u64(); - let mut addr = machine.registers()[A3].to_u64(); + let data = if place == 0 { + 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(false); + } + let cell = cell.unwrap(); + let data = self + .data_loader + .load_cell_data(cell) + .ok_or(VMError::Unexpected)?; + data + } else { + let witness = self.fetch_witness(source, index as usize); + if witness.is_none() { + machine.set_register(A0, Mac::REG::from_u8(INDEX_OUT_OF_BOUND)); + return Ok(true); + } + let witness = witness.unwrap(); + let data = witness.raw_data(); + data + }; + let data = if length == 0 { + data.slice(offset..data.len()) + } else { + data.slice(offset..offset + length) + }; + let argc = machine.registers()[A4].to_u64(); + let mut addr = machine.registers()[A5].to_u64(); let mut argv = Vec::new(); for _ in 0..argc { let target_addr = machine diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 92fd900f32..f4020d0f61 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -201,7 +201,7 @@ mod tests { prelude::*, H256, }; - use ckb_vm::{SupportMachine, machine::DefaultCoreMachine}; + use ckb_vm::{machine::DefaultCoreMachine, SupportMachine}; use ckb_vm::{ machine::{VERSION0, VERSION1}, memory::{FLAG_DIRTY, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE}, @@ -1538,5 +1538,4 @@ mod tests { assert_eq!(result.unwrap(), true); assert_eq!(machine.registers()[A0], 100); } - } diff --git a/script/src/verify.rs b/script/src/verify.rs index 26f6800453..8970f626fe 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -216,6 +216,7 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D self.resolved_cell_deps(), group_inputs, group_outputs, + self.witnesses(), ) } @@ -1822,15 +1823,15 @@ mod tests { #[test] fn check_vm_version() { - let vm_version_cell_data = Bytes::from(std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/vm_version")).unwrap()); + let vm_version_cell_data = Bytes::from( + std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/vm_version")) + .unwrap(), + ); let vm_version_cell = CellOutput::new_builder() - .capacity( - Capacity::bytes(vm_version_cell_data.len()) - .unwrap() - .pack(), - ) - .build(); - let vm_version_script = Script::new_builder().hash_type(ScriptHashType::Data.into()) + .capacity(Capacity::bytes(vm_version_cell_data.len()).unwrap().pack()) + .build(); + let vm_version_script = Script::new_builder() + .hash_type(ScriptHashType::Data.into()) .code_hash(CellOutput::calc_data_hash(&vm_version_cell_data)) .build(); let output = CellOutputBuilder::default() @@ -1866,26 +1867,27 @@ mod tests { } #[test] - fn check_exec() { - let exec_caller_cell_data = Bytes::from(std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_caller")).unwrap()); + fn check_exec_from_cell_data() { + let exec_caller_cell_data = Bytes::from( + std::fs::read( + Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_caller_from_cell_data"), + ) + .unwrap(), + ); let exec_caller_cell = CellOutput::new_builder() - .capacity( - Capacity::bytes(exec_caller_cell_data.len()) - .unwrap() - .pack(), - ) - .build(); + .capacity(Capacity::bytes(exec_caller_cell_data.len()).unwrap().pack()) + .build(); - let exec_callee_cell_data = Bytes::from(std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_callee")).unwrap()); + let exec_callee_cell_data = Bytes::from( + std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_callee")) + .unwrap(), + ); let exec_callee_cell = CellOutput::new_builder() - .capacity( - Capacity::bytes(exec_callee_cell_data.len()) - .unwrap() - .pack(), - ) - .build(); + .capacity(Capacity::bytes(exec_callee_cell_data.len()).unwrap().pack()) + .build(); - let exec_caller_script = Script::new_builder().hash_type(ScriptHashType::Data.into()) + let exec_caller_script = Script::new_builder() + .hash_type(ScriptHashType::Data.into()) .code_hash(CellOutput::calc_data_hash(&exec_caller_cell_data)) .build(); let output = CellOutputBuilder::default() @@ -1924,8 +1926,63 @@ mod tests { let data_loader = DataLoaderWrapper::new(&store); let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); - println!("{:?}", verifier.verify(600000)); assert!(verifier.verify(600000).is_ok()); } + #[test] + fn check_exec_from_witness() { + let exec_caller_cell_data = Bytes::from( + std::fs::read( + Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_caller_from_witness"), + ) + .unwrap(), + ); + let exec_caller_cell = CellOutput::new_builder() + .capacity(Capacity::bytes(exec_caller_cell_data.len()).unwrap().pack()) + .build(); + + let exec_callee = Bytes::from( + std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/exec_callee")) + .unwrap(), + ) + .pack(); + + let exec_caller_script = Script::new_builder() + .hash_type(ScriptHashType::Data.into()) + .code_hash(CellOutput::calc_data_hash(&exec_caller_cell_data)) + .build(); + let output = CellOutputBuilder::default() + .capacity(capacity_bytes!(100).pack()) + .lock(exec_caller_script.clone()) + .build(); + let input = CellInput::new(OutPoint::null(), 0); + + let transaction = TransactionBuilder::default() + .input(input) + .set_witnesses(vec![exec_callee]) + .build(); + + let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) + .transaction_info(default_transaction_info()) + .build(); + let exec_caller_cell = CellMetaBuilder::from_cell_output( + exec_caller_cell.clone(), + exec_caller_cell_data.to_owned(), + ) + .transaction_info(default_transaction_info()) + .build(); + + let rtx = ResolvedTransaction { + transaction, + resolved_cell_deps: vec![exec_caller_cell], + resolved_inputs: vec![dummy_cell], + resolved_dep_groups: vec![], + }; + + let store = new_store(); + let data_loader = DataLoaderWrapper::new(&store); + + let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); + assert!(verifier.verify(600000).is_ok()); + } } diff --git a/script/testdata/exec_callee.c b/script/testdata/exec_callee.c index ed7ffb2ff9..3a815b171c 100644 --- a/script/testdata/exec_callee.c +++ b/script/testdata/exec_callee.c @@ -12,10 +12,4 @@ int main(int argc, char* argv[]) { return 4; } return 0; - - // int sum = 0; - // for (int i = 0; i < argc; i++) { - // sum += strlen(argv[i]); - // } - // return sum; } diff --git a/script/testdata/exec_caller b/script/testdata/exec_caller_from_cell_data similarity index 76% rename from script/testdata/exec_caller rename to script/testdata/exec_caller_from_cell_data index e4dc62a15e8fbe0061e9369b6211b3639eabc0c7..6320a6eb1d037cdfbb942408a4d1813a4961b0e2 100755 GIT binary patch delta 232 zcmbPXJi~ax0e4oHd^D0yJ!RUg!TN)nQE9TGV7X)-6C(o%WPwQr1|B8`1|bGU28YQT1;rVgCZ814 zXKa}KQ_!99#$-n!dBGs4x^$?zlF5Za<&ptVQ{>3aqSi}wr(;!LhO42*8?86NQGGc4H5&ZxpDrgr3?Fgwd%Mvq2!Ubj|PVb%@x z??1$Ec4m6Y#Kgd`S%dWlH>1L2MZt2(941Bv5Xb_P3=BL#c_9Wy2D`}{1;rT~CZ814 zXRMq2Q_!99+GIx|dBFgvx^$?zqRE9q<&u6-Q{Ko>3re4k!a2pEKdx zI?F^k6AV%^Y_6e=OhWbq%Z$Zx+Fm}_;4}GCi^01@CDZG{dc4g7r>~WoIIx;b8~l*F zHWKPwf_Rv_VU2MQt%t%-vW|ycLvUsBe6(*hxmLe$)hMYehak6X2txCD*DJtP4Z*Eh zez-En+7?d4F)K#~p;SN}K>)=hfHf9Q@X<&pj*_&0aO_y@u>0cG2mHw!nRvoA?%~|8 zoH=>7%mWpzL&KN9W*&z>b*Wa<)tXKm*dXSZ+ZMY2u&a*uIJrnD5@NiQe|4$O9PIBQ zVVegSzpKv4`G+q*+Oel=AT-TZ@{2KB(WNn4`6nZ`vd>0rB?BY2qQr=;Tzo356vv_~ zTStXe;%IbL>xfV)jzmjapGM1C$AZ}N`L>E!zDJfLp_V=7MBXqgIOewHC&qdk;%#kP zY-NcNQnJ+8%8gQ64O$+H7JM>hwTPo@-sRk}DpOV1Wi?%RXJ~lcqtWkI#JZY!&4m`7 zIbjzW`?6My@oeFvaH8CIvMClAok(GWTdHcJn5n|r{9x!Z&i zbGL{RbGLvJb2pC?b2o<)b2p&GjBDhIYCHy{|*qqoh{MNl6+&whVbt`{l)8iivoNu=uF>M;% zF04MfSP`@M=*x#;VQb=eZ%DZOMRqKLMs5oUSyPh3AXDK#St)2HJT)cZ7Z0F_|+sZ!AJzS;0O+Icefa7LU z!KrJ%={(xcxiJ0~ORlZ_(MvlnN6)&8Ialp*#<88hR%GxZp@X%NP}s#)p%3n`J3j4i zCbB1#^(NrXIC0-DYc&t76+;eOzW}}58C=5;A|V+$B-I-N6e=&!bFQ79q18!eDWSag z9phYcEHvaWl~ny3FN{$E%v|x8UzN=EeR=PnCYb(>AJy{#JdeQh2t1F#^9cOEBe0Er z7cf6q-MxPOn%qM7=GvvXW%d>JB5)QWCEyJz`+$21i>AQ7RVCi()=gw0+^sWBW?yZzEnFFII}2IztiC3 zjCfYE`gmD@Z;h8(fmjvc{xeo*G6xHMQXCf0hQ%l|@kuk6Ar8wBCw8W#-HTqKcwrj8 zj^gR}s9N3r#CTaWJUTu(o=2!1b}z%Kp2*h}dIMKz+}L{P)^QOwrp?Qr5YIH;e;`iA z^=s8TH^ zvvFj3bib2-6(oJC^C$1eFA&e+C*y+@KbN*o69k6|{xripSnykv^xMQE0n=`Zzd&(^ zXy7FjUrzCeVBmkGxJ>c%|2X9o4^cb&D8CxgRcC$4~Jw zx-Yg+T%))~`<+4Yr!)>1Xum8DKhgW0*cX%z?K*Cmbnd;5b~5?@Gum%8^^?3BNIFUJ zhtwZ(9_dMv6uGfck~Q^!SF7A@%>gxK90W@Om|iN4p)}mhetmE+qpI#3%9;(%txl=Z<$i6m?%UQk*N^+@ z3Hqg7a+9Y~1vX(c1kG9ibsN3)N|Fx@nHU}-`br`3Xb$)lGWp{xf`N!t*3^u5S|1Eb zhTA^X7gROy$=)U~!i&^YcU65Z^mqfWSUc*nj7=?(v(r53tYO2q8%nYkD`8qZv`_Jx&+o(RfFQn_U`uEcG**)?MJ%HK0 zj`3N)tiBBk1Y_f8?}3l0ei$5Xb|D3brW$tk^mGQLduznp7m M9Y|Kja=QM10JR@p+yDRo literal 0 HcmV?d00001 diff --git a/script/testdata/exec_caller_from_witness.c b/script/testdata/exec_caller_from_witness.c new file mode 100644 index 0000000000..4a12a0da52 --- /dev/null +++ b/script/testdata/exec_caller_from_witness.c @@ -0,0 +1,26 @@ +static inline long __internal_syscall(long n, long _a0, long _a1, long _a2, + long _a3, long _a4, long _a5) { + register long a0 asm("a0") = _a0; + register long a1 asm("a1") = _a1; + register long a2 asm("a2") = _a2; + register long a3 asm("a3") = _a3; + register long a4 asm("a4") = _a4; + register long a5 asm("a5") = _a5; + register long syscall_id asm("a7") = n; + + asm volatile("scall" + : "+r"(a0) + : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(syscall_id)); + return a0; +} + +#define syscall(n, a, b, c, d, e, f) \ + __internal_syscall(n, (long)(a), (long)(b), (long)(c), (long)(d), (long)(e), \ + (long)(f)) + +int main() { + int argc = 3; + char *argv[] = {"a", "b", "c"}; + syscall(2043, 0, 1, 1, 0, argc, argv); + return -1; +} From ec59a90011b95c00cd989b377e6a4910de498d7e Mon Sep 17 00:00:00 2001 From: Boyu Yang Date: Fri, 28 May 2021 02:45:26 +0800 Subject: [PATCH 04/14] chore: new vm and new syscalls: use crates.io; fix compilation, test and clippy --- Cargo.lock | 10 +++-- script/Cargo.toml | 4 +- script/src/syscalls/exec.rs | 11 ++--- script/src/verify.rs | 82 ++++++++++++++++++++----------------- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81967092cb..c3ceaa9a7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1492,8 +1492,9 @@ dependencies = [ [[package]] name = "ckb-vm" -version = "0.19.1" -source = "git+https://github.com/mohanson/ckb-vm?branch=develop#09e1e21995acaea3f75d26e144e652b4c4ba67ea" +version = "0.20.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca63668a4a96a895c5324edbc3f827781d043a79d82789d590f7d611ce9dff4a" dependencies = [ "byteorder", "bytes 1.0.1", @@ -1511,8 +1512,9 @@ dependencies = [ [[package]] name = "ckb-vm-definitions" -version = "0.19.1" -source = "git+https://github.com/mohanson/ckb-vm?branch=develop#09e1e21995acaea3f75d26e144e652b4c4ba67ea" +version = "0.20.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1983bc408f08c00d8907e602f24bb54caf7b9aadbdbb20624d24a2a2fc6e3efb" [[package]] name = "clap" diff --git a/script/Cargo.toml b/script/Cargo.toml index 6f788993f0..57941c1632 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -21,8 +21,8 @@ ckb-traits = { path = "../traits", version = "= 0.43.0-pre" } byteorder = "1.3.1" ckb-types = {path = "../util/types", version = "= 0.43.0-pre"} ckb-hash = {path = "../util/hash", version = "= 0.43.0-pre"} -ckb-vm-definitions = { git = "https://github.com/mohanson/ckb-vm", branch="develop" } -ckb-vm = { git = "https://github.com/mohanson/ckb-vm", branch="develop", default-features = false } +ckb-vm-definitions = "0.20.0-alpha" +ckb-vm = { version = "0.20.0-alpha", default-features = false } faster-hex = "0.4" ckb-logger = { path = "../util/logger", version = "= 0.43.0-pre", optional = true } serde = { version = "1.0", features = ["derive"] } diff --git a/script/src/syscalls/exec.rs b/script/src/syscalls/exec.rs index 55cce65662..88d9aa7a34 100644 --- a/script/src/syscalls/exec.rs +++ b/script/src/syscalls/exec.rs @@ -123,7 +123,7 @@ impl<'a, Mac: SupportMachine, DL: CellDataProvider> Syscalls for Exec<'a, D let source = Source::parse_from_u64(machine.registers()[A1].to_u64())?; let place = machine.registers()[A2].to_u64(); let bounds = machine.registers()[A3].to_u64(); - let offset = bounds as usize >> 32; + let offset = (bounds >> 32) as usize; let length = bounds as u32 as usize; let data = if place == 0 { @@ -133,11 +133,9 @@ impl<'a, Mac: SupportMachine, DL: CellDataProvider> Syscalls for Exec<'a, D return Ok(false); } let cell = cell.unwrap(); - let data = self - .data_loader + self.data_loader .load_cell_data(cell) - .ok_or(VMError::Unexpected)?; - data + .ok_or(VMError::Unexpected)? } else { let witness = self.fetch_witness(source, index as usize); if witness.is_none() { @@ -145,8 +143,7 @@ impl<'a, Mac: SupportMachine, DL: CellDataProvider> Syscalls for Exec<'a, D return Ok(true); } let witness = witness.unwrap(); - let data = witness.raw_data(); - data + witness.raw_data() }; let data = if length == 0 { data.slice(offset..data.len()) diff --git a/script/src/verify.rs b/script/src/verify.rs index fb20a6913b..f3a60d0fc3 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -23,6 +23,12 @@ use ckb_types::{ packed::{Byte32, Byte32Vec, BytesVec, CellInputVec, CellOutput, OutPoint, Script}, prelude::*, }; +#[cfg(not(has_asm))] +use ckb_vm::{ + machine::VERSION0, DefaultCoreMachine, DefaultMachineBuilder, Error as VMInternalError, + InstructionCycleFunc, SparseMemory, SupportMachine, Syscalls, TraceMachine, WXorXMemory, ISA_B, + ISA_IMC, +}; #[cfg(has_asm)] use ckb_vm::{ machine::{ @@ -32,18 +38,13 @@ use ckb_vm::{ DefaultMachineBuilder, Error as VMInternalError, InstructionCycleFunc, SupportMachine, Syscalls, ISA_B, ISA_IMC, }; -#[cfg(not(has_asm))] -use ckb_vm::{ - DefaultCoreMachine, DefaultMachineBuilder, Error as VMInternalError, InstructionCycleFunc, - SparseMemory, SupportMachine, Syscalls, TraceMachine, WXorXMemory, -}; use std::collections::HashMap; use std::convert::TryFrom; #[cfg(has_asm)] type CoreMachineType = Box; #[cfg(not(has_asm))] -type CoreMachineType = DefaultCoreMachine>>; +type CoreMachineType = DefaultCoreMachine>>; enum Binaries { Unique((Byte32, Bytes)), @@ -491,7 +492,7 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D #[cfg(has_asm)] let core_machine = AsmCoreMachine::new(ISA_IMC | ISA_B, VERSION0, max_cycles); #[cfg(not(has_asm))] - let core_machine = DefaultCoreMachine::>>::new( + let core_machine = DefaultCoreMachine::>>::new( ISA_IMC | ISA_B, VERSION0, max_cycles, @@ -1984,7 +1985,7 @@ mod tests { .build(); let output = CellOutputBuilder::default() .capacity(capacity_bytes!(100).pack()) - .lock(vm_version_script.clone()) + .lock(vm_version_script) .build(); let input = CellInput::new(OutPoint::null(), 0); @@ -1993,12 +1994,10 @@ mod tests { let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) .transaction_info(default_transaction_info()) .build(); - let vm_version_cell = CellMetaBuilder::from_cell_output( - vm_version_cell.clone(), - vm_version_cell_data.to_owned(), - ) - .transaction_info(default_transaction_info()) - .build(); + let vm_version_cell = + CellMetaBuilder::from_cell_output(vm_version_cell, vm_version_cell_data) + .transaction_info(default_transaction_info()) + .build(); let rtx = ResolvedTransaction { transaction, @@ -2009,8 +2008,13 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); + let consensus = ConsensusBuilder::default().build(); + let tx_env = { + let header = HeaderView::new_advanced_builder().build(); + TxVerifyEnv::new_commit(&header) + }; - let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); + let verifier = TransactionScriptsVerifier::new(&rtx, &consensus, &data_loader, &tx_env); assert!(verifier.verify(6000).is_ok()); } @@ -2040,7 +2044,7 @@ mod tests { .build(); let output = CellOutputBuilder::default() .capacity(capacity_bytes!(100).pack()) - .lock(exec_caller_script.clone()) + .lock(exec_caller_script) .build(); let input = CellInput::new(OutPoint::null(), 0); @@ -2049,19 +2053,15 @@ mod tests { let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) .transaction_info(default_transaction_info()) .build(); - let exec_caller_cell = CellMetaBuilder::from_cell_output( - exec_caller_cell.clone(), - exec_caller_cell_data.to_owned(), - ) - .transaction_info(default_transaction_info()) - .build(); + let exec_caller_cell = + CellMetaBuilder::from_cell_output(exec_caller_cell, exec_caller_cell_data) + .transaction_info(default_transaction_info()) + .build(); - let exec_callee_cell = CellMetaBuilder::from_cell_output( - exec_callee_cell.clone(), - exec_callee_cell_data.to_owned(), - ) - .transaction_info(default_transaction_info()) - .build(); + let exec_callee_cell = + CellMetaBuilder::from_cell_output(exec_callee_cell, exec_callee_cell_data) + .transaction_info(default_transaction_info()) + .build(); let rtx = ResolvedTransaction { transaction, @@ -2072,8 +2072,13 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); + let consensus = ConsensusBuilder::default().build(); + let tx_env = { + let header = HeaderView::new_advanced_builder().build(); + TxVerifyEnv::new_commit(&header) + }; - let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); + let verifier = TransactionScriptsVerifier::new(&rtx, &consensus, &data_loader, &tx_env); assert!(verifier.verify(600000).is_ok()); } @@ -2101,7 +2106,7 @@ mod tests { .build(); let output = CellOutputBuilder::default() .capacity(capacity_bytes!(100).pack()) - .lock(exec_caller_script.clone()) + .lock(exec_caller_script) .build(); let input = CellInput::new(OutPoint::null(), 0); @@ -2113,12 +2118,10 @@ mod tests { let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) .transaction_info(default_transaction_info()) .build(); - let exec_caller_cell = CellMetaBuilder::from_cell_output( - exec_caller_cell.clone(), - exec_caller_cell_data.to_owned(), - ) - .transaction_info(default_transaction_info()) - .build(); + let exec_caller_cell = + CellMetaBuilder::from_cell_output(exec_caller_cell, exec_caller_cell_data) + .transaction_info(default_transaction_info()) + .build(); let rtx = ResolvedTransaction { transaction, @@ -2129,8 +2132,13 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); + let consensus = ConsensusBuilder::default().build(); + let tx_env = { + let header = HeaderView::new_advanced_builder().build(); + TxVerifyEnv::new_commit(&header) + }; - let verifier = TransactionScriptsVerifier::new(&rtx, &data_loader); + let verifier = TransactionScriptsVerifier::new(&rtx, &consensus, &data_loader, &tx_env); assert!(verifier.verify(600000).is_ok()); } } From fa40d1cb6cc535ff75cf2cca3553548fb608ca3b Mon Sep 17 00:00:00 2001 From: Boyu Yang Date: Tue, 15 Jun 2021 15:17:31 +0800 Subject: [PATCH 05/14] feat(hardfork): vm version selection, vm version 1 and syscalls 2 --- benches/benches/benchmarks/util.rs | 2 +- chain/src/tests/block_assembler.rs | 4 +- chain/src/tests/reward.rs | 4 +- chain/src/tests/util.rs | 4 +- devtools/doc/rpc.py | 3 +- rpc/src/module/pool.rs | 18 +- script/src/error.rs | 8 + script/src/ill_transaction_checker.rs | 32 ++- script/src/syscalls/mod.rs | 4 +- script/src/verify.rs | 243 +++++++++++++----- script/testdata/vm_version | Bin 6400 -> 6408 bytes script/testdata/vm_version.c | 5 +- spec/src/hardfork.rs | 6 +- spec/src/lib.rs | 4 +- test/src/main.rs | 4 +- test/src/node.rs | 2 +- test/src/specs/hardfork/v2021/cell_deps.rs | 2 +- test/src/specs/mining/bootstrap.rs | 4 +- test/src/specs/tx_pool/mod.rs | 2 +- .../specs/tx_pool/send_multisig_secp_tx.rs | 2 +- test/template/specs/integration.toml | 1 + util/app-config/src/configs/tx_pool.rs | 58 ++++- util/jsonrpc-types/src/blockchain.rs | 176 ++++++++++++- util/jsonrpc-types/src/lib.rs | 8 +- util/jsonrpc-types/src/primitive.rs | 8 +- util/jsonrpc-types/src/uints.rs | 11 +- util/test-chain-utils/src/chain.rs | 6 +- util/types/src/core/blockchain.rs | 17 +- util/types/src/core/hardfork.rs | 16 ++ util/types/src/extension/check_data.rs | 4 +- 30 files changed, 528 insertions(+), 130 deletions(-) mode change 100755 => 100644 script/testdata/vm_version diff --git a/benches/benches/benchmarks/util.rs b/benches/benches/benchmarks/util.rs index d99f76f20a..1f050f0b67 100644 --- a/benches/benches/benchmarks/util.rs +++ b/benches/benches/benchmarks/util.rs @@ -225,7 +225,7 @@ lazy_static! { let script = Script::new_builder() .code_hash(CellOutput::calc_data_hash(&data)) .args(Bytes::from(PUBKEY_HASH.as_bytes()).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); (cell, data, script) diff --git a/chain/src/tests/block_assembler.rs b/chain/src/tests/block_assembler.rs index 6117bfc5fd..97a5f690c2 100644 --- a/chain/src/tests/block_assembler.rs +++ b/chain/src/tests/block_assembler.rs @@ -33,7 +33,9 @@ fn start_chain(consensus: Option) -> (ChainController, Shared) { let config = BlockAssemblerConfig { code_hash: h256!("0x0"), args: Default::default(), - hash_type: ScriptHashType::Data, + hash_type: ScriptHashType::Data { + vm_version: 0.into(), + }, message: Default::default(), }; let (shared, mut pack) = builder diff --git a/chain/src/tests/reward.rs b/chain/src/tests/reward.rs index 89318cff80..a41667243d 100644 --- a/chain/src/tests/reward.rs +++ b/chain/src/tests/reward.rs @@ -179,14 +179,14 @@ fn finalize_reward() { let bob = ScriptBuilder::default() .args(bob_args) .code_hash(always_success_script.code_hash()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let alice_args: packed::Bytes = Bytes::from(b"a11ce".to_vec()).pack(); let alice = ScriptBuilder::default() .args(alice_args) .code_hash(always_success_script.code_hash()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); for i in 1..23 { diff --git a/chain/src/tests/util.rs b/chain/src/tests/util.rs index f3acf25321..b48494854e 100644 --- a/chain/src/tests/util.rs +++ b/chain/src/tests/util.rs @@ -115,7 +115,9 @@ pub(crate) fn start_chain(consensus: Option) -> (ChainController, Sha let config = BlockAssemblerConfig { code_hash: h256!("0x0"), args: Default::default(), - hash_type: ScriptHashType::Data, + hash_type: ScriptHashType::Data { + vm_version: 0.into(), + }, message: Default::default(), }; diff --git a/devtools/doc/rpc.py b/devtools/doc/rpc.py index 9ce9ab8000..72dcd1e513 100755 --- a/devtools/doc/rpc.py +++ b/devtools/doc/rpc.py @@ -500,7 +500,8 @@ def handle_endtag(self, tag): if self.variant_parser is not None: self.variant_parser.handle_endtag(tag) if self.variant_parser.completed(): - self.variants.append((self.next_variant, self.variant_parser)) + if self.next_variant not in [v[0] for v in self.variants]: + self.variants.append((self.next_variant, self.variant_parser)) self.next_variant = None self.variant_parser = None diff --git a/rpc/src/module/pool.rs b/rpc/src/module/pool.rs index dcb58201ef..563bfc1c51 100644 --- a/rpc/src/module/pool.rs +++ b/rpc/src/module/pool.rs @@ -3,9 +3,9 @@ use ckb_chain_spec::consensus::Consensus; use ckb_jsonrpc_types::{OutputsValidator, RawTxPool, Transaction, TxPoolInfo}; use ckb_logger::error; use ckb_script::IllTransactionChecker; -use ckb_shared::shared::Shared; +use ckb_shared::{shared::Shared, Snapshot}; use ckb_types::{core, packed, prelude::*, H256}; -use ckb_verification::{Since, SinceMetric}; +use ckb_verification::{Since, SinceMetric, TxVerifyEnv}; use jsonrpc_core::Result; use jsonrpc_derive::rpc; use std::convert::TryInto; @@ -258,7 +258,13 @@ impl PoolRpc for PoolRpcImpl { } if self.reject_ill_transactions { - if let Err(e) = IllTransactionChecker::new(&tx).check() { + let snapshot: &Snapshot = &self.shared.snapshot(); + let consensus = snapshot.consensus(); + let tx_env = { + let tip_header = snapshot.tip_header(); + TxVerifyEnv::new_submit(&tip_header) + }; + if let Err(e) = IllTransactionChecker::new(&tx, consensus, &tx_env).check() { return Err(RPCError::custom_with_data( RPCError::PoolRejectedTransactionByIllTransactionChecker, "The transaction is rejected by IllTransactionChecker", @@ -496,7 +502,7 @@ mod tests { assert!(validator.validate(&tx).is_err()); // invalid hash type - let tx = build_tx(&type_hash, core::ScriptHashType::Data, vec![1; 20]); + let tx = build_tx(&type_hash, core::ScriptHashType::Data(0), vec![1; 20]); assert!(validator.validate(&tx).is_err()); // invalid code hash @@ -528,7 +534,7 @@ mod tests { assert!(validator.validate(&tx).is_err()); // invalid hash type - let tx = build_tx(&type_hash, core::ScriptHashType::Data, vec![1; 20]); + let tx = build_tx(&type_hash, core::ScriptHashType::Data(0), vec![1; 20]); assert!(validator.validate(&tx).is_err()); // invalid since args format @@ -583,7 +589,7 @@ mod tests { core::ScriptHashType::Type, vec![1; 20], &type_type_hash, - core::ScriptHashType::Data, + core::ScriptHashType::Data(0), ); assert!(validator.validate(&tx).is_err()); diff --git a/script/src/error.rs b/script/src/error.rs index f965cb24f5..8c512a77c9 100644 --- a/script/src/error.rs +++ b/script/src/error.rs @@ -26,6 +26,14 @@ pub enum ScriptError { #[error("Known bugs encountered in output {1}: {0}")] EncounteredKnownBugs(String, usize), + /// InvalidScriptHashType + #[error("InvalidScriptHashType: {0}")] + InvalidScriptHashType(String), + + /// InvalidVmVersion + #[error("Invalid vm version {0}")] + InvalidVmVersion(u8), + /// Known bugs are detected in transaction script outputs #[error("VM Internal Error: {0}")] VMInternalError(String), diff --git a/script/src/ill_transaction_checker.rs b/script/src/ill_transaction_checker.rs index b5e0916c7e..2dc8a5d742 100644 --- a/script/src/ill_transaction_checker.rs +++ b/script/src/ill_transaction_checker.rs @@ -1,8 +1,9 @@ -use crate::ScriptError; +use crate::{verify_env::TxVerifyEnv, ScriptError}; use byteorder::{ByteOrder, LittleEndian}; +use ckb_chain_spec::consensus::Consensus; use ckb_types::core::TransactionView; use ckb_vm::{ - instructions::{b, extract_opcode, i, m, rvc, Instruction, Itype}, + instructions::{extract_opcode, i, m, rvc, Instruction, Itype}, registers::ZERO, }; use ckb_vm_definitions::instructions as insts; @@ -13,18 +14,30 @@ const CKB_VM_ISSUE_92: &str = "https://github.com/nervosnetwork/ckb-vm/issues/92 /// Ill formed transactions checker. pub struct IllTransactionChecker<'a> { tx: &'a TransactionView, + consensus: &'a Consensus, + tx_env: &'a TxVerifyEnv, } impl<'a> IllTransactionChecker<'a> { /// Creates the checker for a transaction. - pub fn new(tx: &'a TransactionView) -> Self { - IllTransactionChecker { tx } + pub fn new(tx: &'a TransactionView, consensus: &'a Consensus, tx_env: &'a TxVerifyEnv) -> Self { + IllTransactionChecker { + tx, + consensus, + tx_env, + } } /// Checks whether the transaction is ill formed. pub fn check(&self) -> Result<(), ScriptError> { - for (i, data) in self.tx.outputs_data().into_iter().enumerate() { - IllScriptChecker::new(&data.raw_data(), i).check()?; + let proposal_window = self.consensus.tx_proposal_window(); + let epoch_number = self.tx_env.epoch_number(proposal_window); + let hardfork_switch = self.consensus.hardfork_switch(); + // TODO ckb2021 require-confirmation We couldn't known if user run the code in vm v0 or vm v1. + if !hardfork_switch.is_vm_version_1_and_syscalls_2_enabled(epoch_number) { + for (i, data) in self.tx.outputs_data().into_iter().enumerate() { + IllScriptChecker::new(&data.raw_data(), i).check()?; + } } Ok(()) } @@ -87,12 +100,7 @@ impl<'a> IllScriptChecker<'a> { } i = LittleEndian::read_u32(&self.data[pc as usize..]); } - let factories = [ - rvc::factory::, - i::factory::, - m::factory::, - b::factory::, - ]; + let factories = [rvc::factory::, i::factory::, m::factory::]; for factory in &factories { if let Some(instruction) = factory(i) { return (Some(instruction), len); diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index 50426d73bc..a3ec78e28d 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -834,7 +834,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(data.to_owned()).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let hash = script.calc_script_hash(); let data = hash.raw_data(); @@ -889,7 +889,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(data.to_owned()).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let h = script.calc_script_hash(); let hash = h.as_bytes(); diff --git a/script/src/verify.rs b/script/src/verify.rs index 9afc602430..7ab02a7ce0 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -23,21 +23,15 @@ use ckb_types::{ packed::{Byte32, Byte32Vec, BytesVec, CellInputVec, CellOutput, OutPoint, Script}, prelude::*, }; -#[cfg(not(has_asm))] -use ckb_vm::{ - machine::VERSION0, DefaultCoreMachine, DefaultMachineBuilder, Error as VMInternalError, - InstructionCycleFunc, SparseMemory, SupportMachine, Syscalls, TraceMachine, WXorXMemory, ISA_B, - ISA_IMC, -}; #[cfg(has_asm)] +use ckb_vm::machine::asm::{AsmCoreMachine, AsmMachine}; use ckb_vm::{ - machine::{ - asm::{AsmCoreMachine, AsmMachine}, - VERSION0, - }, + machine::{VERSION0, VERSION1}, DefaultMachineBuilder, Error as VMInternalError, InstructionCycleFunc, SupportMachine, - Syscalls, ISA_B, ISA_IMC, + Syscalls, ISA_B, ISA_IMC, ISA_MOP, }; +#[cfg(not(has_asm))] +use ckb_vm::{DefaultCoreMachine, SparseMemory, TraceMachine, WXorXMemory}; use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; @@ -359,8 +353,10 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D /// Extracts actual script binary either in dep cells. pub fn extract_script(&self, script: &'a Script) -> Result { - match ScriptHashType::try_from(script.hash_type()).expect("checked data") { - ScriptHashType::Data => { + let script_hash_type = ScriptHashType::try_from(script.hash_type()) + .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?; + match script_hash_type { + ScriptHashType::Data(_) => { if let Some(lazy) = self.binaries_by_data_hash.get(&script.code_hash()) { Ok(lazy.access(self.data_loader)) } else { @@ -393,6 +389,37 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D } } + /// Select the ISA and the version number of the new machine. + pub fn select_machine_options(&self, script: &'a Script) -> Result<(u8, u32), ScriptError> { + let proposal_window = self.consensus.tx_proposal_window(); + let epoch_number = self.tx_env.epoch_number(proposal_window); + let hardfork_switch = self.consensus.hardfork_switch(); + let is_vm_version_1_and_syscalls_2_enabled = + hardfork_switch.is_vm_version_1_and_syscalls_2_enabled(epoch_number); + let script_hash_type = ScriptHashType::try_from(script.hash_type()) + .map_err(|err| ScriptError::InvalidScriptHashType(err.to_string()))?; + match script_hash_type { + ScriptHashType::Data(version) => { + if !is_vm_version_1_and_syscalls_2_enabled && version > 0 { + Err(ScriptError::InvalidVmVersion(version)) + } else { + match version { + 0 => Ok((ISA_IMC, VERSION0)), + 1 => Ok((ISA_IMC | ISA_B | ISA_MOP, VERSION1)), + _ => Err(ScriptError::InvalidVmVersion(version)), + } + } + } + ScriptHashType::Type => { + if is_vm_version_1_and_syscalls_2_enabled { + Ok((ISA_IMC | ISA_B | ISA_MOP, VERSION1)) + } else { + Ok((ISA_IMC, VERSION0)) + } + } + } + } + /// Verifies the transaction by running scripts. /// /// ## Params @@ -483,13 +510,11 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D /// Prepares syscalls. pub fn generate_syscalls( &'a self, + version: u32, script_group: &'a ScriptGroup, ) -> Vec + 'a)>> { let current_script_hash = script_group.script.calc_script_hash(); - vec![ - Box::new(self.build_vm_version()), - Box::new(self.build_current_cycles()), - Box::new(self.build_exec(&script_group.input_indices, &script_group.output_indices)), + let mut syscalls: Vec + 'a)>> = vec![ Box::new(self.build_load_script_hash(current_script_hash.clone())), Box::new(self.build_load_tx()), Box::new( @@ -508,23 +533,30 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D ), ), Box::new(Debugger::new(current_script_hash, &self.debug_printer)), - ] + ]; + if version >= VERSION1 { + syscalls.append(&mut vec![ + Box::new(self.build_vm_version()), + Box::new(self.build_current_cycles()), + Box::new( + self.build_exec(&script_group.input_indices, &script_group.output_indices), + ), + ]) + } + syscalls } fn run(&self, script_group: &ScriptGroup, max_cycles: Cycle) -> Result { let program = self.extract_script(&script_group.script)?; + let (isa, version) = self.select_machine_options(&script_group.script)?; #[cfg(has_asm)] - let core_machine = AsmCoreMachine::new(ISA_IMC | ISA_B, VERSION0, max_cycles); + let core_machine = AsmCoreMachine::new(isa, version, max_cycles); #[cfg(not(has_asm))] - let core_machine = DefaultCoreMachine::>>::new( - ISA_IMC | ISA_B, - VERSION0, - max_cycles, - ); + let core_machine = CoreMachineType::new(isa, version, max_cycles); let machine_builder = DefaultMachineBuilder::::new(core_machine) .instruction_cycle_func(self.cost_model()); let machine_builder = self - .generate_syscalls(script_group) + .generate_syscalls(version, script_group) .into_iter() .fold(machine_builder, |builder, syscall| builder.syscall(syscall)); let default_machine = machine_builder.build(); @@ -566,8 +598,9 @@ mod tests { use ckb_store::{data_loader_wrapper::DataLoaderWrapper, ChainDB}; use ckb_types::{ core::{ - capacity_bytes, cell::CellMetaBuilder, Capacity, Cycle, DepType, HeaderView, - ScriptHashType, TransactionBuilder, TransactionInfo, + capacity_bytes, cell::CellMetaBuilder, hardfork::HardForkSwitch, Capacity, Cycle, + DepType, EpochNumberWithFraction, HeaderView, ScriptHashType, TransactionBuilder, + TransactionInfo, }, h256, packed::{ @@ -690,7 +723,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -730,7 +766,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(args).pack()) .code_hash(code_hash.pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let input = CellInput::new(OutPoint::null(), 0); @@ -755,7 +791,10 @@ mod tests { }; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -801,7 +840,7 @@ mod tests { Some( Script::new_builder() .code_hash(h256!("0x123456abcd90").pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(), ) .pack(), @@ -841,7 +880,10 @@ mod tests { }; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -876,7 +918,7 @@ mod tests { Some( Script::new_builder() .code_hash(h256!("0x123456abcd90").pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(), ) .pack(), @@ -898,7 +940,7 @@ mod tests { Some( Script::new_builder() .code_hash(h256!("0x123456abcd90").pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(), ) .pack(), @@ -938,7 +980,10 @@ mod tests { }; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -982,7 +1027,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(args).pack()) .code_hash(code_hash.pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let input = CellInput::new(OutPoint::null(), 0); @@ -1008,7 +1053,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1040,7 +1088,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(args).pack()) .code_hash(blake2b_256(&buffer).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let input = CellInput::new(OutPoint::null(), 0); @@ -1066,7 +1114,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1112,13 +1163,13 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(args).pack()) .code_hash(blake2b_256(&buffer).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let output_data = Bytes::default(); let output = CellOutputBuilder::default() .lock( Script::new_builder() - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(), ) .type_(Some(script).pack()) @@ -1155,7 +1206,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1201,7 +1255,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(args).pack()) .code_hash(blake2b_256(&buffer).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let output = CellOutputBuilder::default() .type_(Some(script).pack()) @@ -1235,7 +1289,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1266,7 +1323,7 @@ mod tests { let script = Script::new_builder() .args(Bytes::from(args).pack()) .code_hash(blake2b_256(&buffer).pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let dep_out_point = OutPoint::new(h256!("0x123").pack(), 8); @@ -1305,7 +1362,10 @@ mod tests { }; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1374,7 +1434,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1441,7 +1504,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1519,7 +1585,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1583,7 +1652,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1662,7 +1734,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1747,7 +1822,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1821,7 +1899,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -1983,7 +2064,10 @@ mod tests { let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { let header = HeaderView::new_advanced_builder().build(); TxVerifyEnv::new_commit(&header) @@ -2006,7 +2090,7 @@ mod tests { .capacity(Capacity::bytes(vm_version_cell_data.len()).unwrap().pack()) .build(); let vm_version_script = Script::new_builder() - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(1).into()) .code_hash(CellOutput::calc_data_hash(&vm_version_cell_data)) .build(); let output = CellOutputBuilder::default() @@ -2032,11 +2116,22 @@ mod tests { resolved_dep_groups: vec![], }; + let fork_at = 10; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled() + .as_builder() + .rfc_pr_0237(fork_at) + .build() + .unwrap(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { - let header = HeaderView::new_advanced_builder().build(); + let epoch = EpochNumberWithFraction::new(fork_at, 0, 1); + let header = HeaderView::new_advanced_builder() + .epoch(epoch.pack()) + .build(); TxVerifyEnv::new_commit(&header) }; @@ -2065,7 +2160,7 @@ mod tests { .build(); let exec_caller_script = Script::new_builder() - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(1).into()) .code_hash(CellOutput::calc_data_hash(&exec_caller_cell_data)) .build(); let output = CellOutputBuilder::default() @@ -2096,11 +2191,22 @@ mod tests { resolved_dep_groups: vec![], }; + let fork_at = 10; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled() + .as_builder() + .rfc_pr_0237(fork_at) + .build() + .unwrap(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { - let header = HeaderView::new_advanced_builder().build(); + let epoch = EpochNumberWithFraction::new(fork_at, 0, 1); + let header = HeaderView::new_advanced_builder() + .epoch(epoch.pack()) + .build(); TxVerifyEnv::new_commit(&header) }; @@ -2127,7 +2233,7 @@ mod tests { .pack(); let exec_caller_script = Script::new_builder() - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(1).into()) .code_hash(CellOutput::calc_data_hash(&exec_caller_cell_data)) .build(); let output = CellOutputBuilder::default() @@ -2156,11 +2262,22 @@ mod tests { resolved_dep_groups: vec![], }; + let fork_at = 10; let store = new_store(); let data_loader = DataLoaderWrapper::new(&store); - let consensus = ConsensusBuilder::default().build(); + let hardfork_switch = HardForkSwitch::new_without_any_enabled() + .as_builder() + .rfc_pr_0237(fork_at) + .build() + .unwrap(); + let consensus = ConsensusBuilder::default() + .hardfork_switch(hardfork_switch) + .build(); let tx_env = { - let header = HeaderView::new_advanced_builder().build(); + let epoch = EpochNumberWithFraction::new(fork_at, 0, 1); + let header = HeaderView::new_advanced_builder() + .epoch(epoch.pack()) + .build(); TxVerifyEnv::new_commit(&header) }; diff --git a/script/testdata/vm_version b/script/testdata/vm_version old mode 100755 new mode 100644 index 3111cbe34bae68925e544e9cbb42cbd60aaa8919..b355c5d06a47a6908916e600b825471581517aea GIT binary patch delta 963 zcmZWo&ubGw6rM>oKi0-{Hfy?Bn>JZNjiN-;V#q;~l*U|}9>nxwYipr`B)Ox0`B|&#q z&!qE5Jj5POB#dK^`{J{yuZs~fT6$%zWdWtJOj1|eY5k7|?tR3Dp% z@-ws0Bl{y7ny8+eX?>f&HOG1+cd19#!>+K7M1m85k_6(lTt~Q<&k|8ax9Yj073k!uM z@Y(8VOZ~61`@;2vdZcM+wcxb$8T_8OI(wY=$A)>_9HwA+cj3F{?=tT-uaXdr^R@{| zKt1q~20%F)cDo6~sL{BMJjmq{RiTEQkF+s9MG+>Hfzt_in#SF#DfQyQzPr10?Es;3j;`^L{ zIYpxjP*;-r4T)>+2+ag)81a!!*g}VPpZ|<-cn3d}m@+PM7RBmSFhZK%5IKIq-nJdB z^C=h*+p_kO0BGAiy}^F!5AC6{YnKL) zpW#HJi@mdE&pmz14xn{rKxBc^NnpzZ_&imEJW6bXiMYv5k$%SR2`{$oIZOo>i^a08 ztdvhH!+hxb@vr*T^ZA=c6wrlY*)GO!G=!wEwf80d^ml2kw1HBcq90Y9=zo#*a;Ft| z%*1*Hw&RES)`-5J9(F4mGE`bio3L~23R9L22QRW&afTa4|AsCyYOw|DNMa_DySK}sH&J( zWz!gY#g!3Fkg}_4bUFaS7*175f*@n_zxZ>)RXF^Uz_%(le+llZMnk5&*CW*F zy7zL2(UFhHG>-F{Z2E}o0x+b!2z9lL6^)yQ$GYgDGXWR9hPIYrtGG`w-7)UV6r+L! z7WOsHUgA)jO`q`cZ+ZEbJl+ZK{s&dnXhH8_A;Qh^dEFl|hKO;K(D8b8k28hs2xsp* zuOhV|D`PsAQT*)&tj2PR?0v9|ZSwqa7}$^H@Iy?+6rWf8@1OAk>>(D3N T#H6vr4zN1XZPF{)H|G8V5owoH diff --git a/script/testdata/vm_version.c b/script/testdata/vm_version.c index b66b1e8e4f..66c33c2bbe 100644 --- a/script/testdata/vm_version.c +++ b/script/testdata/vm_version.c @@ -19,5 +19,8 @@ static inline long __internal_syscall(long n, long _a0, long _a1, long _a2, (long)(f)) int main() { - return syscall(2041, 0, 0, 0, 0, 0, 0); + if (syscall(2041, 0, 0, 0, 0, 0, 0) == 1) { + return 0; + } + return 1; } diff --git a/spec/src/hardfork.rs b/spec/src/hardfork.rs index e8021f0784..76593ffb74 100644 --- a/spec/src/hardfork.rs +++ b/spec/src/hardfork.rs @@ -20,6 +20,8 @@ pub struct HardForkConfig { pub rfc_pr_0223: Option, /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) pub rfc_pr_0224: Option, + /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) + pub rfc_pr_0237: Option, } macro_rules! check_default { @@ -66,7 +68,8 @@ impl HardForkConfig { .rfc_pr_0221(check_default!(self, rfc_pr_0221, ckb2021)) .rfc_pr_0222(check_default!(self, rfc_pr_0222, ckb2021)) .rfc_pr_0223(check_default!(self, rfc_pr_0223, ckb2021)) - .rfc_pr_0224(check_default!(self, rfc_pr_0224, ckb2021)); + .rfc_pr_0224(check_default!(self, rfc_pr_0224, ckb2021)) + .rfc_pr_0237(check_default!(self, rfc_pr_0237, ckb2021)); Ok(builder) } @@ -79,6 +82,7 @@ impl HardForkConfig { .rfc_pr_0222(self.rfc_pr_0222.unwrap_or(default)) .rfc_pr_0223(self.rfc_pr_0223.unwrap_or(default)) .rfc_pr_0224(self.rfc_pr_0224.unwrap_or(default)) + .rfc_pr_0237(self.rfc_pr_0237.unwrap_or(default)) .build() } } diff --git a/spec/src/lib.rs b/spec/src/lib.rs index aad021eab3..0b389a1073 100644 --- a/spec/src/lib.rs +++ b/spec/src/lib.rs @@ -618,7 +618,7 @@ impl ChainSpec { }) { match ScriptHashType::try_from(lock_script.hash_type()).expect("checked data") { - ScriptHashType::Data => { + ScriptHashType::Data(_) => { if !data_hashes.contains_key(&lock_script.code_hash()) { return Err(format!( "Invalid lock script: code_hash={}, hash_type=data", @@ -712,7 +712,7 @@ impl ChainSpec { let special_issued_lock = packed::Script::new_builder() .args(secp_lock_arg(&Privkey::from(SPECIAL_CELL_PRIVKEY.clone())).pack()) .code_hash(CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL.clone().pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let special_issued_cell = packed::CellOutput::new_builder() .capacity(special_cell_capacity.pack()) diff --git a/test/src/main.rs b/test/src/main.rs index d2dc560bec..524fe5dcd7 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -424,7 +424,7 @@ fn all_specs() -> Vec> { )), Box::new(SendSecpTxUseDepGroup::new( "send_secp_tx_use_dep_group_data_hash", - ScriptHashType::Data, + ScriptHashType::Data(0), )), Box::new(SendSecpTxUseDepGroup::new( "send_secp_tx_use_dep_group_type_hash", @@ -432,7 +432,7 @@ fn all_specs() -> Vec> { )), Box::new(SendMultiSigSecpTxUseDepGroup::new( "send_multisig_secp_tx_use_dep_group_data_hash", - ScriptHashType::Data, + ScriptHashType::Data(0), )), Box::new(SendMultiSigSecpTxUseDepGroup::new( "send_multisig_secp_tx_use_dep_group_type_hash", diff --git a/test/src/node.rs b/test/src/node.rs index 88524c9683..ff37dec155 100644 --- a/test/src/node.rs +++ b/test/src/node.rs @@ -192,7 +192,7 @@ impl Node { let always_success_code_hash = CellOutput::calc_data_hash(&always_success_raw); Script::new_builder() .code_hash(always_success_code_hash) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build() } diff --git a/test/src/specs/hardfork/v2021/cell_deps.rs b/test/src/specs/hardfork/v2021/cell_deps.rs index 85d9daf114..e0fb64181f 100644 --- a/test/src/specs/hardfork/v2021/cell_deps.rs +++ b/test/src/specs/hardfork/v2021/cell_deps.rs @@ -433,7 +433,7 @@ impl NewScript { fn as_data_script(&self) -> packed::Script { packed::Script::new_builder() .code_hash(self.data_hash.clone()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build() } diff --git a/test/src/specs/mining/bootstrap.rs b/test/src/specs/mining/bootstrap.rs index 9873428d91..bb4bc82702 100644 --- a/test/src/specs/mining/bootstrap.rs +++ b/test/src/specs/mining/bootstrap.rs @@ -24,7 +24,7 @@ impl Spec for BootstrapCellbase { let miner = packed::Script::new_builder() .args(Bytes::from(vec![2, 1]).pack()) .code_hash(h256!("0xa2").pack()) - .hash_type(ScriptHashType::Data.into()) + .hash_type(ScriptHashType::Data(0).into()) .build(); let is_bootstrap_cellbase = |number| { @@ -61,7 +61,7 @@ impl Spec for BootstrapCellbase { config.block_assembler = Some(BlockAssemblerConfig { code_hash: h256!("0xa2"), args: JsonBytes::from_bytes(Bytes::from(vec![2, 1])), - hash_type: ScriptHashType::Data.into(), + hash_type: ScriptHashType::Data(0).into(), message: Default::default(), }); } diff --git a/test/src/specs/tx_pool/mod.rs b/test/src/specs/tx_pool/mod.rs index 29edb92d6c..0b056af91f 100644 --- a/test/src/specs/tx_pool/mod.rs +++ b/test/src/specs/tx_pool/mod.rs @@ -54,7 +54,7 @@ fn type_lock_script_code_hash() -> H256 { } fn new_block_assembler_config(lock_arg: Bytes, hash_type: ScriptHashType) -> BlockAssemblerConfig { - let code_hash = if hash_type == ScriptHashType::Data { + let code_hash = if hash_type == ScriptHashType::Data(0) { CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL.clone() } else { type_lock_script_code_hash() diff --git a/test/src/specs/tx_pool/send_multisig_secp_tx.rs b/test/src/specs/tx_pool/send_multisig_secp_tx.rs index 6f54a8af3d..d3018b660a 100644 --- a/test/src/specs/tx_pool/send_multisig_secp_tx.rs +++ b/test/src/specs/tx_pool/send_multisig_secp_tx.rs @@ -141,7 +141,7 @@ fn type_lock_script_code_hash() -> H256 { } fn new_block_assembler_config(lock_arg: Bytes, hash_type: ScriptHashType) -> BlockAssemblerConfig { - let code_hash = if hash_type == ScriptHashType::Data { + let code_hash = if hash_type == ScriptHashType::Data(0) { CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL.clone() } else { type_lock_script_code_hash() diff --git a/test/template/specs/integration.toml b/test/template/specs/integration.toml index cf1b5ff7b7..9a92cb248d 100644 --- a/test/template/specs/integration.toml +++ b/test/template/specs/integration.toml @@ -73,6 +73,7 @@ rfc_pr_0221 = 9_223_372_036_854_775_807 rfc_pr_0222 = 9_223_372_036_854_775_807 rfc_pr_0223 = 9_223_372_036_854_775_807 rfc_pr_0224 = 9_223_372_036_854_775_807 +rfc_pr_0237 = 9_223_372_036_854_775_807 [pow] func = "Dummy" diff --git a/util/app-config/src/configs/tx_pool.rs b/util/app-config/src/configs/tx_pool.rs index 170d3d9f4c..318aa5cf58 100644 --- a/util/app-config/src/configs/tx_pool.rs +++ b/util/app-config/src/configs/tx_pool.rs @@ -1,5 +1,5 @@ use ckb_chain_spec::consensus::TWO_IN_TWO_OUT_CYCLES; -use ckb_jsonrpc_types::{FeeRateDef, JsonBytes, ScriptHashType}; +use ckb_jsonrpc_types::{FeeRateDef, JsonBytes, ScriptHashType, ScriptHashTypeShadow, VmVersion}; use ckb_types::core::{Cycle, FeeRate}; use ckb_types::H256; use serde::{Deserialize, Serialize}; @@ -52,13 +52,69 @@ impl Default for TxPoolConfig { /// /// The block assembler section tells CKB how to claim the miner rewards. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(try_from = "BlockAssemblerConfigShadow")] pub struct BlockAssemblerConfig { /// The miner lock script code hash. pub code_hash: H256, /// The miner lock script hash type. + #[serde(flatten)] pub hash_type: ScriptHashType, /// The miner lock script args. pub args: JsonBytes, /// An arbitrary message to be added into the cellbase transaction. pub message: JsonBytes, } + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct BlockAssemblerConfigShadow { + code_hash: H256, + #[serde(rename = "hash_type")] + hash_type_shadow: ScriptHashTypeShadow, + #[serde(rename = "vm_version")] + vm_version_opt: Option, + args: JsonBytes, + message: JsonBytes, +} + +struct BlockAssemblerConfigValidationError(&'static str); + +impl std::fmt::Display for BlockAssemblerConfigValidationError { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.0) + } +} + +impl std::convert::TryFrom for BlockAssemblerConfig { + type Error = BlockAssemblerConfigValidationError; + fn try_from(shadow: BlockAssemblerConfigShadow) -> Result { + let BlockAssemblerConfigShadow { + code_hash, + hash_type_shadow, + vm_version_opt, + args, + message, + } = shadow; + let hash_type = match hash_type_shadow { + ScriptHashTypeShadow::Data => { + let vm_version = vm_version_opt.unwrap_or_default(); + ScriptHashType::Data { vm_version } + } + ScriptHashTypeShadow::Type => { + if vm_version_opt.is_some() { + return Err(BlockAssemblerConfigValidationError( + "vm version is not allowed for hash-type \"type\".", + )); + } + ScriptHashType::Type + } + }; + let script = BlockAssemblerConfig { + code_hash, + hash_type, + args, + message, + }; + Ok(script) + } +} diff --git a/util/jsonrpc-types/src/blockchain.rs b/util/jsonrpc-types/src/blockchain.rs index f04095c86f..517b42908c 100644 --- a/util/jsonrpc-types/src/blockchain.rs +++ b/util/jsonrpc-types/src/blockchain.rs @@ -1,7 +1,7 @@ use crate::bytes::JsonBytes; use crate::{ BlockNumber, Byte32, Capacity, Cycle, EpochNumber, EpochNumberWithFraction, ProposalShortId, - Timestamp, Uint128, Uint32, Uint64, Version, + Timestamp, Uint128, Uint32, Uint64, Version, VmVersion, }; use ckb_types::{core, packed, prelude::*, H256}; use serde::{Deserialize, Serialize}; @@ -16,24 +16,38 @@ use std::fmt; /// and [Upgradable Script](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0022-transaction-structure/0022-transaction-structure.md#upgradable-script) /// in the RFC *CKB Transaction Structure*. #[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] -#[serde(rename_all = "snake_case")] +#[serde(rename_all = "snake_case", deny_unknown_fields, tag = "hash_type")] pub enum ScriptHashType { /// Type "data" matches script code via cell data hash. - Data, + Data { + /// CKB-VM version. + #[serde(default, skip_serializing_if = "VmVersion::is_default")] + vm_version: VmVersion, + }, /// Type "type" matches script code via cell type script hash. Type, } +#[doc(hidden)] +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ScriptHashTypeShadow { + Data, + Type, +} + impl Default for ScriptHashType { fn default() -> Self { - ScriptHashType::Data + ScriptHashType::Data { + vm_version: Default::default(), + } } } impl From for core::ScriptHashType { fn from(json: ScriptHashType) -> Self { match json { - ScriptHashType::Data => core::ScriptHashType::Data, + ScriptHashType::Data { vm_version } => core::ScriptHashType::Data(vm_version.into()), ScriptHashType::Type => core::ScriptHashType::Type, } } @@ -42,7 +56,9 @@ impl From for core::ScriptHashType { impl From for ScriptHashType { fn from(core: core::ScriptHashType) -> ScriptHashType { match core { - core::ScriptHashType::Data => ScriptHashType::Data, + core::ScriptHashType::Data(v) => ScriptHashType::Data { + vm_version: v.into(), + }, core::ScriptHashType::Type => ScriptHashType::Type, } } @@ -51,7 +67,9 @@ impl From for ScriptHashType { impl fmt::Display for ScriptHashType { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { - ScriptHashType::Data => write!(f, "data"), + ScriptHashType::Data { vm_version } => { + write!(f, "data {{ vm_version: {} }}", vm_version) + } ScriptHashType::Type => write!(f, "type"), } } @@ -71,16 +89,68 @@ impl fmt::Display for ScriptHashType { /// # "#).unwrap(); /// ``` #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] -#[serde(deny_unknown_fields)] +#[serde(try_from = "ScriptShadow")] pub struct Script { /// The hash used to match the script code. pub code_hash: H256, /// Specifies how to use the `code_hash` to match the script code. + #[serde(flatten)] pub hash_type: ScriptHashType, /// Arguments for script. pub args: JsonBytes, } +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct ScriptShadow { + code_hash: H256, + #[serde(rename = "hash_type")] + hash_type_shadow: ScriptHashTypeShadow, + #[serde(rename = "vm_version")] + vm_version_opt: Option, + args: JsonBytes, +} + +struct ScriptValidationError(&'static str); + +impl std::fmt::Display for ScriptValidationError { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "{}", self.0) + } +} + +impl std::convert::TryFrom for Script { + type Error = ScriptValidationError; + fn try_from(shadow: ScriptShadow) -> Result { + let ScriptShadow { + code_hash, + hash_type_shadow, + vm_version_opt, + args, + } = shadow; + let hash_type = match hash_type_shadow { + ScriptHashTypeShadow::Data => { + let vm_version = vm_version_opt.unwrap_or_default(); + ScriptHashType::Data { vm_version } + } + ScriptHashTypeShadow::Type => { + if vm_version_opt.is_some() { + return Err(ScriptValidationError( + "vm version is not allowed for hash-type \"type\".", + )); + } + ScriptHashType::Type + } + }; + let script = Script { + code_hash, + hash_type, + args, + }; + Ok(script) + } +} + impl From