diff --git a/script/src/verify/tests/ckb_latest/features_since_v2023.rs b/script/src/verify/tests/ckb_latest/features_since_v2023.rs index f35ebad614..d6539cfcb1 100644 --- a/script/src/verify/tests/ckb_latest/features_since_v2023.rs +++ b/script/src/verify/tests/ckb_latest/features_since_v2023.rs @@ -1268,3 +1268,55 @@ fn check_spawn_cycles() { assert_eq!(result.unwrap(), 1524861); } } + +fn spawn_io_test(io_size: u64, enable_check: bool) -> Result { + let script_version = SCRIPT_VERSION; + + let mut args = vec![0u8; 16]; + args[..8].copy_from_slice(&io_size.to_le_bytes()); + args[8] = enable_check as u8; + + let (cell, data_hash) = load_cell_from_path("testdata/spawn_io_cycles"); + let script = Script::new_builder() + .hash_type(script_version.data_hash_type().into()) + .code_hash(data_hash) + .args(Bytes::copy_from_slice(&args).pack()) + .build(); + let output = CellOutputBuilder::default() + .capacity(capacity_bytes!(100).pack()) + .lock(script) + .build(); + let input = CellInput::new(OutPoint::null(), 0); + + let transaction = TransactionBuilder::default().input(input).build(); + let dummy_cell = create_dummy_cell(output); + + let rtx = ResolvedTransaction { + transaction, + resolved_cell_deps: vec![cell], + resolved_inputs: vec![dummy_cell], + resolved_dep_groups: vec![], + }; + let verifier = TransactionScriptsVerifierWithEnv::new(); + verifier.verify_without_limit(script_version, &rtx) +} + +#[test] +fn check_spawn_io_cycles() { + if SCRIPT_VERSION != ScriptVersion::V2 { + return; + } + + let offset_size = 1024; + let r = spawn_io_test(128, true); + r.unwrap(); + let r = spawn_io_test(128 + offset_size, true); + r.unwrap(); + + let r = spawn_io_test(128, false); + let cycles1 = r.unwrap(); + let r = spawn_io_test(128 + offset_size, false); + let cycles2 = r.unwrap(); + + assert_eq!(cycles2 - cycles1, offset_size / 2); +} diff --git a/script/testdata/Makefile b/script/testdata/Makefile index 3fead0fe69..71325a06c4 100644 --- a/script/testdata/Makefile +++ b/script/testdata/Makefile @@ -73,7 +73,8 @@ ALL_BINS := jalr_zero \ spawn_dag \ spawn_fuzzing \ spawn_huge_swap \ - spawn_cycles + spawn_cycles \ + spawn_io_cycles ALL_LIBS := is_even.lib \ add1.lib sub1.lib mul2.lib div2.lib @@ -160,3 +161,4 @@ spawn_dag: spawn_dag.c spawn_dag.h spawn_dag_escape_encoding.h spawn_fuzzing: spawn_fuzzing.c spawn_utils.h spawn_huge_swap: spawn_huge_swap.c spawn_utils.h spawn_cycles: spawn_cycles.c spawn_utils.h +spawn_io_cycles: spawn_io_cycles.c spawn_utils.h diff --git a/script/testdata/spawn_io_cycles b/script/testdata/spawn_io_cycles new file mode 100755 index 0000000000..f9edfd9dfb Binary files /dev/null and b/script/testdata/spawn_io_cycles differ diff --git a/script/testdata/spawn_io_cycles.c b/script/testdata/spawn_io_cycles.c new file mode 100644 index 0000000000..046e6fb6c6 --- /dev/null +++ b/script/testdata/spawn_io_cycles.c @@ -0,0 +1,109 @@ +#include +#include + +#include "ckb_syscalls.h" +#include "spawn_utils.h" + +const uint64_t SYSCALL_CYCLES_BASE = 500; +const uint64_t SPAWN_EXTRA_CYCLES_BASE = 100000; +const uint64_t SPAWN_YIELD_CYCLES_BASE = 800; + +int tic() { + static uint64_t tic = 0; + uint64_t cur_cycles = ckb_current_cycles(); + uint64_t toc = cur_cycles - tic; + tic = cur_cycles; + return toc; +} + +uint64_t cal_cycles(uint64_t nbase, uint64_t yield, uint64_t extra) { + uint64_t r = 0; + r += SYSCALL_CYCLES_BASE * nbase; + r += SPAWN_YIELD_CYCLES_BASE * yield; + r += SPAWN_EXTRA_CYCLES_BASE * extra; + return r; +} + +uint64_t cal_cycles_floor(uint64_t nbase, uint64_t yield, uint64_t extra) { + return cal_cycles(nbase, yield, extra); +} + +uint64_t cal_cycles_upper(uint64_t nbase, uint64_t yield, uint64_t extra) { + return cal_cycles(nbase, yield, extra) + 8192; +} + +#define BUFFER_SIZE 1024 * 4 + +typedef struct { + uint64_t io_size; + bool check_buffer; +} ScriptArgs; + +int parent(ScriptArgs* args, uint8_t* buffer) { + int err = 0; + const char* argv[] = {"", 0}; + uint64_t fds[2] = {0}; + uint64_t pid = 0; + err = full_spawn(0, 1, argv, fds, &pid); + CHECK(err); + + uint64_t buf_len = args->io_size; + + err = ckb_read(fds[CKB_STDIN], buffer, &buf_len); + CHECK(err); + CHECK2(buf_len == args->io_size, -1); + if (args->check_buffer) { + for (size_t i = 0; i < args->io_size; i++) + CHECK2(buffer[i] == (uint8_t)i, -1); + } + + int8_t exit_code = 0; + err = ckb_wait(pid, &exit_code); + CHECK(err); + CHECK(exit_code); + +exit: + return err; +} + +int child(ScriptArgs* args, uint8_t* buffer) { + int err = 0; + uint64_t inherited_fds[2]; + size_t inherited_fds_length = 2; + err = ckb_inherited_file_descriptors(inherited_fds, &inherited_fds_length); + CHECK(err); + + uint64_t buf_len = args->io_size; + + if (args->check_buffer) { + for (size_t i = 0; i < args->io_size; i++) buffer[i] = i; + } + + err = ckb_write(inherited_fds[CKB_STDOUT], buffer, &buf_len); + + CHECK(err); + CHECK2(buf_len == args->io_size, -1); +exit: + return err; +} + +int main() { + int err = 0; + ScriptArgs script_args; + size_t script_args_length = sizeof(script_args); + err = load_script_args((uint8_t*)&script_args, &script_args_length); + CHECK(err); + CHECK2(script_args_length == sizeof(script_args), -1); + + uint64_t cid = ckb_process_id(); + uint8_t buffer[BUFFER_SIZE] = {0}; + + if (cid == 0) { + return parent(&script_args, buffer); + } else { + return child(&script_args, buffer); + } + +exit: + return err; +}