From 5099985a0f21339bae3609562e79126ac0b0912b Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Fri, 7 Mar 2025 10:53:07 +0800 Subject: [PATCH 01/18] update cases --- demo/Cargo.toml | 3 + demo/contracts/ckb-script-ipc-demo/Cargo.toml | 9 +- demo/contracts/ckb-script-ipc-test/Cargo.toml | 7 +- demo/contracts/first-contract/Cargo.toml | 2 +- .../contracts/test_invalid_request/.gitignore | 2 + .../contracts/test_invalid_request/Cargo.toml | 20 ++++ demo/contracts/test_invalid_request/Makefile | 77 +++++++++++++++ demo/contracts/test_invalid_request/README.md | 6 ++ .../test_invalid_request/src/client.rs | 92 ++++++++++++++++++ .../contracts/test_invalid_request/src/def.rs | 8 ++ .../test_invalid_request/src/error.rs | 6 ++ .../test_invalid_request/src/server.rs | 71 ++++++++++++++ demo/contracts/test_loop_request/.gitignore | 2 + demo/contracts/test_loop_request/Cargo.toml | 20 ++++ demo/contracts/test_loop_request/Makefile | 77 +++++++++++++++ demo/contracts/test_loop_request/README.md | 6 ++ .../contracts/test_loop_request/src/client.rs | 94 +++++++++++++++++++ demo/contracts/test_loop_request/src/def.rs | 8 ++ demo/contracts/test_loop_request/src/error.rs | 6 ++ .../contracts/test_loop_request/src/server.rs | 71 ++++++++++++++ .../test_single_loop_request/.gitignore | 2 + .../test_single_loop_request/Cargo.toml | 20 ++++ .../test_single_loop_request/Makefile | 77 +++++++++++++++ .../test_single_loop_request/README.md | 6 ++ .../test_single_loop_request/src/client.rs | 67 +++++++++++++ .../test_single_loop_request/src/def.rs | 8 ++ .../test_single_loop_request/src/error.rs | 6 ++ .../test_single_loop_request/src/server.rs | 71 ++++++++++++++ src/tests/ckb_script_ipc_demo.rs | 67 +++++++++++++ 29 files changed, 901 insertions(+), 10 deletions(-) create mode 100644 demo/contracts/test_invalid_request/.gitignore create mode 100644 demo/contracts/test_invalid_request/Cargo.toml create mode 100644 demo/contracts/test_invalid_request/Makefile create mode 100644 demo/contracts/test_invalid_request/README.md create mode 100644 demo/contracts/test_invalid_request/src/client.rs create mode 100644 demo/contracts/test_invalid_request/src/def.rs create mode 100644 demo/contracts/test_invalid_request/src/error.rs create mode 100644 demo/contracts/test_invalid_request/src/server.rs create mode 100644 demo/contracts/test_loop_request/.gitignore create mode 100644 demo/contracts/test_loop_request/Cargo.toml create mode 100644 demo/contracts/test_loop_request/Makefile create mode 100644 demo/contracts/test_loop_request/README.md create mode 100644 demo/contracts/test_loop_request/src/client.rs create mode 100644 demo/contracts/test_loop_request/src/def.rs create mode 100644 demo/contracts/test_loop_request/src/error.rs create mode 100644 demo/contracts/test_loop_request/src/server.rs create mode 100644 demo/contracts/test_single_loop_request/.gitignore create mode 100644 demo/contracts/test_single_loop_request/Cargo.toml create mode 100644 demo/contracts/test_single_loop_request/Makefile create mode 100644 demo/contracts/test_single_loop_request/README.md create mode 100644 demo/contracts/test_single_loop_request/src/client.rs create mode 100644 demo/contracts/test_single_loop_request/src/def.rs create mode 100644 demo/contracts/test_single_loop_request/src/error.rs create mode 100644 demo/contracts/test_single_loop_request/src/server.rs diff --git a/demo/Cargo.toml b/demo/Cargo.toml index b26b5e5..1801b32 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -9,6 +9,9 @@ members = [ "contracts/ckb-script-ipc-demo", "contracts/ckb-script-ipc-test", "tests", + "contracts/test_invalid_request", + "contracts/test_loop_request", + "contracts/test_single_loop_request" ] [profile.release] diff --git a/demo/contracts/ckb-script-ipc-demo/Cargo.toml b/demo/contracts/ckb-script-ipc-demo/Cargo.toml index 8e2961c..1cf5e3b 100644 --- a/demo/contracts/ckb-script-ipc-demo/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-demo/Cargo.toml @@ -4,13 +4,12 @@ version = "1.0.0" edition = "2021" [dependencies] -ckb-std = { version = "0.16", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -#ckb-script-ipc-common = { path = "../../crates/ckb-script-ipc-common" } -ckb-script-ipc-common = "1.0.0" - +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = "1.0.0" +ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +log = "0.4.25" [[bin]] name = "bin1" diff --git a/demo/contracts/ckb-script-ipc-test/Cargo.toml b/demo/contracts/ckb-script-ipc-test/Cargo.toml index ba4ad2a..2893c74 100644 --- a/demo/contracts/ckb-script-ipc-test/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-test/Cargo.toml @@ -4,12 +4,11 @@ version = "1.0.0" edition = "2021" [dependencies] -ckb-std = { version = "0.16", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -#ckb-script-ipc-common = { path = "../../crates/ckb-script-ipc-common" } -ckb-script-ipc-common = "1.0.0" +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = "1.0.0" +ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/first-contract/Cargo.toml b/demo/contracts/first-contract/Cargo.toml index 67c6f7b..47928a2 100644 --- a/demo/contracts/first-contract/Cargo.toml +++ b/demo/contracts/first-contract/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -ckb-std = "0.16.3" +ckb-std = "0.17.0" [features] native-simulator = ["ckb-std/native-simulator"] diff --git a/demo/contracts/test_invalid_request/.gitignore b/demo/contracts/test_invalid_request/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/test_invalid_request/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/test_invalid_request/Cargo.toml b/demo/contracts/test_invalid_request/Cargo.toml new file mode 100644 index 0000000..8421c05 --- /dev/null +++ b/demo/contracts/test_invalid_request/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test_invalid_request" +version = "1.0.0" +edition = "2021" + +[dependencies] +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } + +serde = { version = "1.0", default-features = false, features = ["derive"] } +ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +log = "0.4.25" + +[[bin]] +name = "test_invalid_request-client" +path = "src/client.rs" + +[[bin]] +name = "test_invalid_request-serve" +path = "src/server.rs" \ No newline at end of file diff --git a/demo/contracts/test_invalid_request/Makefile b/demo/contracts/test_invalid_request/Makefile new file mode 100644 index 0000000..58027f7 --- /dev/null +++ b/demo/contracts/test_invalid_request/Makefile @@ -0,0 +1,77 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "test_invalid_request-client" "test_invalid_request-serve" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/test_invalid_request/README.md b/demo/contracts/test_invalid_request/README.md new file mode 100644 index 0000000..c267109 --- /dev/null +++ b/demo/contracts/test_invalid_request/README.md @@ -0,0 +1,6 @@ +# ckb-script-ipc-demo + +A demo for ckb-script-ipc, showcasing inter-process communication (IPC) between +CKB scripts. This demo contains both client and server scripts in a single +codebase for simplicity and ease of understanding. It demonstrates how to use +ckb-script-ipc to enable communication between different CKB scripts. diff --git a/demo/contracts/test_invalid_request/src/client.rs b/demo/contracts/test_invalid_request/src/client.rs new file mode 100644 index 0000000..dfb445a --- /dev/null +++ b/demo/contracts/test_invalid_request/src/client.rs @@ -0,0 +1,92 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, log::info}; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::def::WorldClient; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + let (read_pipe1, write_pipe1) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + let mut client2 = WorldClient::new(read_pipe1, write_pipe1); + + // invoke + // invoke normal case + let ret = client.hello("world \0\n\\n\\\\''''' ///@@".into()).unwrap(); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test error case + let ret = client.hello("error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test server_error case + let ret = client.hello("server_error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test ckb_sys_error case + let ret = client.hello("ckb_sys_error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test unknown error case + let ret = client.hello("unknown".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test second client + let data = client2.get_data(); + info!("data:{:?}",data); + + Ok(()) +} diff --git a/demo/contracts/test_invalid_request/src/def.rs b/demo/contracts/test_invalid_request/src/def.rs new file mode 100644 index 0000000..23884a2 --- /dev/null +++ b/demo/contracts/test_invalid_request/src/def.rs @@ -0,0 +1,8 @@ +use alloc::string::String; + +// IPC definition, it can be shared between client and server +#[ckb_script_ipc::service] +pub trait World { + fn hello(name: String) -> Result; + fn get_data() -> usize; +} diff --git a/demo/contracts/test_invalid_request/src/error.rs b/demo/contracts/test_invalid_request/src/error.rs new file mode 100644 index 0000000..2c81059 --- /dev/null +++ b/demo/contracts/test_invalid_request/src/error.rs @@ -0,0 +1,6 @@ +#[repr(i8)] +pub enum Error { + Unknown = 1, + CkbSysError, + ServerError, +} diff --git a/demo/contracts/test_invalid_request/src/server.rs b/demo/contracts/test_invalid_request/src/server.rs new file mode 100644 index 0000000..975f7c6 --- /dev/null +++ b/demo/contracts/test_invalid_request/src/server.rs @@ -0,0 +1,71 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{log::info}; + +use crate::def::World; +use alloc::{format, string::String}; +use ckb_script_ipc_common::spawn::run_server; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +struct WorldServer { + pub data: usize, +} + +impl WorldServer { + fn new() -> Self { + WorldServer { + data: 2 + } + } +} + +impl World for WorldServer { + // method implementation + fn hello(&mut self, name: String) -> Result { + self.data += 1; + if name == "error" { + Err(1) + } else if name == "server_error" { + Err(Error::ServerError as u64) + } else if name == "ckb_sys_error" { + Err(Error::CkbSysError as u64) + } else if name == "unknown" { + Err(Error::Unknown as u64) + } else { + Ok(format!("hello, {}", name)) + } + } + + fn get_data(&mut self) -> usize { + return self.data; + } +} + +pub fn server_run() -> Result<(), Error> { + let world = WorldServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} \ No newline at end of file diff --git a/demo/contracts/test_loop_request/.gitignore b/demo/contracts/test_loop_request/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/test_loop_request/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/test_loop_request/Cargo.toml b/demo/contracts/test_loop_request/Cargo.toml new file mode 100644 index 0000000..205adc1 --- /dev/null +++ b/demo/contracts/test_loop_request/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test_loop_request" +version = "1.0.0" +edition = "2021" + +[dependencies] +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } + +serde = { version = "1.0", default-features = false, features = ["derive"] } +ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +log = "0.4.25" + +[[bin]] +name = "test_loop_request-client" +path = "src/client.rs" + +[[bin]] +name = "test_loop_request-serve" +path = "src/server.rs" \ No newline at end of file diff --git a/demo/contracts/test_loop_request/Makefile b/demo/contracts/test_loop_request/Makefile new file mode 100644 index 0000000..f67ebc7 --- /dev/null +++ b/demo/contracts/test_loop_request/Makefile @@ -0,0 +1,77 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "test_loop_request-client" "test_loop_request-serve" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/test_loop_request/README.md b/demo/contracts/test_loop_request/README.md new file mode 100644 index 0000000..c267109 --- /dev/null +++ b/demo/contracts/test_loop_request/README.md @@ -0,0 +1,6 @@ +# ckb-script-ipc-demo + +A demo for ckb-script-ipc, showcasing inter-process communication (IPC) between +CKB scripts. This demo contains both client and server scripts in a single +codebase for simplicity and ease of understanding. It demonstrates how to use +ckb-script-ipc to enable communication between different CKB scripts. diff --git a/demo/contracts/test_loop_request/src/client.rs b/demo/contracts/test_loop_request/src/client.rs new file mode 100644 index 0000000..9886f00 --- /dev/null +++ b/demo/contracts/test_loop_request/src/client.rs @@ -0,0 +1,94 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, log::info}; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::def::WorldClient; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + let (read_pipe1, write_pipe1) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + let mut client2 = WorldClient::new(read_pipe1, write_pipe1); + + // invoke in an infinite loop + loop { + // invoke normal case + let ret = client.hello("world \0\n\\n\\\\''''' ///@@".into()).unwrap(); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test error case + let ret = client.hello("error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test server_error case + let ret = client.hello("server_error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test ckb_sys_error case + let ret = client.hello("ckb_sys_error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test unknown error case + let ret = client.hello("unknown".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}",data); + + // test second client + let data = client2.get_data(); + info!("data:{:?}",data); + } + + Ok(()) +} diff --git a/demo/contracts/test_loop_request/src/def.rs b/demo/contracts/test_loop_request/src/def.rs new file mode 100644 index 0000000..23884a2 --- /dev/null +++ b/demo/contracts/test_loop_request/src/def.rs @@ -0,0 +1,8 @@ +use alloc::string::String; + +// IPC definition, it can be shared between client and server +#[ckb_script_ipc::service] +pub trait World { + fn hello(name: String) -> Result; + fn get_data() -> usize; +} diff --git a/demo/contracts/test_loop_request/src/error.rs b/demo/contracts/test_loop_request/src/error.rs new file mode 100644 index 0000000..2c81059 --- /dev/null +++ b/demo/contracts/test_loop_request/src/error.rs @@ -0,0 +1,6 @@ +#[repr(i8)] +pub enum Error { + Unknown = 1, + CkbSysError, + ServerError, +} diff --git a/demo/contracts/test_loop_request/src/server.rs b/demo/contracts/test_loop_request/src/server.rs new file mode 100644 index 0000000..975f7c6 --- /dev/null +++ b/demo/contracts/test_loop_request/src/server.rs @@ -0,0 +1,71 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{log::info}; + +use crate::def::World; +use alloc::{format, string::String}; +use ckb_script_ipc_common::spawn::run_server; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +struct WorldServer { + pub data: usize, +} + +impl WorldServer { + fn new() -> Self { + WorldServer { + data: 2 + } + } +} + +impl World for WorldServer { + // method implementation + fn hello(&mut self, name: String) -> Result { + self.data += 1; + if name == "error" { + Err(1) + } else if name == "server_error" { + Err(Error::ServerError as u64) + } else if name == "ckb_sys_error" { + Err(Error::CkbSysError as u64) + } else if name == "unknown" { + Err(Error::Unknown as u64) + } else { + Ok(format!("hello, {}", name)) + } + } + + fn get_data(&mut self) -> usize { + return self.data; + } +} + +pub fn server_run() -> Result<(), Error> { + let world = WorldServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} \ No newline at end of file diff --git a/demo/contracts/test_single_loop_request/.gitignore b/demo/contracts/test_single_loop_request/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/test_single_loop_request/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/test_single_loop_request/Cargo.toml b/demo/contracts/test_single_loop_request/Cargo.toml new file mode 100644 index 0000000..110e53c --- /dev/null +++ b/demo/contracts/test_single_loop_request/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test_single_loop_request" +version = "1.0.0" +edition = "2021" + +[dependencies] +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } + +serde = { version = "1.0", default-features = false, features = ["derive"] } +ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +log = "0.4.25" + +[[bin]] +name = "test_single_loop_request-client" +path = "src/client.rs" + +[[bin]] +name = "test_single_loop_request-serve" +path = "src/server.rs" \ No newline at end of file diff --git a/demo/contracts/test_single_loop_request/Makefile b/demo/contracts/test_single_loop_request/Makefile new file mode 100644 index 0000000..d183b64 --- /dev/null +++ b/demo/contracts/test_single_loop_request/Makefile @@ -0,0 +1,77 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "test_single_loop_request-client" "test_single_loop_request-serve" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/test_single_loop_request/README.md b/demo/contracts/test_single_loop_request/README.md new file mode 100644 index 0000000..c267109 --- /dev/null +++ b/demo/contracts/test_single_loop_request/README.md @@ -0,0 +1,6 @@ +# ckb-script-ipc-demo + +A demo for ckb-script-ipc, showcasing inter-process communication (IPC) between +CKB scripts. This demo contains both client and server scripts in a single +codebase for simplicity and ease of understanding. It demonstrates how to use +ckb-script-ipc to enable communication between different CKB scripts. diff --git a/demo/contracts/test_single_loop_request/src/client.rs b/demo/contracts/test_single_loop_request/src/client.rs new file mode 100644 index 0000000..d248a61 --- /dev/null +++ b/demo/contracts/test_single_loop_request/src/client.rs @@ -0,0 +1,67 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, log::info}; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::def::WorldClient; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + let (read_pipe1, write_pipe1) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + let mut client2 = WorldClient::new(read_pipe1, write_pipe1); + + // invoke in an infinite loop + loop { + // invoke normal case + let ret = client.hello("world \0\n\\n\\\\''''' ///@@".into()).unwrap(); + info!("IPC response: {:?}", ret); + let data = client2.get_data(); + info!("data:{:?}",data); + + } + + Ok(()) +} diff --git a/demo/contracts/test_single_loop_request/src/def.rs b/demo/contracts/test_single_loop_request/src/def.rs new file mode 100644 index 0000000..23884a2 --- /dev/null +++ b/demo/contracts/test_single_loop_request/src/def.rs @@ -0,0 +1,8 @@ +use alloc::string::String; + +// IPC definition, it can be shared between client and server +#[ckb_script_ipc::service] +pub trait World { + fn hello(name: String) -> Result; + fn get_data() -> usize; +} diff --git a/demo/contracts/test_single_loop_request/src/error.rs b/demo/contracts/test_single_loop_request/src/error.rs new file mode 100644 index 0000000..2c81059 --- /dev/null +++ b/demo/contracts/test_single_loop_request/src/error.rs @@ -0,0 +1,6 @@ +#[repr(i8)] +pub enum Error { + Unknown = 1, + CkbSysError, + ServerError, +} diff --git a/demo/contracts/test_single_loop_request/src/server.rs b/demo/contracts/test_single_loop_request/src/server.rs new file mode 100644 index 0000000..975f7c6 --- /dev/null +++ b/demo/contracts/test_single_loop_request/src/server.rs @@ -0,0 +1,71 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{log::info}; + +use crate::def::World; +use alloc::{format, string::String}; +use ckb_script_ipc_common::spawn::run_server; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +struct WorldServer { + pub data: usize, +} + +impl WorldServer { + fn new() -> Self { + WorldServer { + data: 2 + } + } +} + +impl World for WorldServer { + // method implementation + fn hello(&mut self, name: String) -> Result { + self.data += 1; + if name == "error" { + Err(1) + } else if name == "server_error" { + Err(Error::ServerError as u64) + } else if name == "ckb_sys_error" { + Err(Error::CkbSysError as u64) + } else if name == "unknown" { + Err(Error::Unknown as u64) + } else { + Ok(format!("hello, {}", name)) + } + } + + fn get_data(&mut self) -> usize { + return self.data; + } +} + +pub fn server_run() -> Result<(), Error> { + let world = WorldServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} \ No newline at end of file diff --git a/src/tests/ckb_script_ipc_demo.rs b/src/tests/ckb_script_ipc_demo.rs index 5129611..54fa97a 100644 --- a/src/tests/ckb_script_ipc_demo.rs +++ b/src/tests/ckb_script_ipc_demo.rs @@ -32,3 +32,70 @@ fn test_parse_method_with_return_type() { let ret1 = ct.context.should_be_passed(&tx, 1000000); println!("ret:{:?}", ret1); } + + +#[test] +fn test_invalid_request() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_invalid_request-serve"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_invalid_request-client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_passed(&tx, 10000000); + println!("ret:{:?}", ret1); +} + + +#[test] +fn test_forever_loop() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_loop_request-serve"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_loop_request-client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_failed(&tx, 1000000000); + println!("ret:{:?}", ret1); +} + +#[test] +fn test_single_forever_loop() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-sreve"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_failed(&tx, 1000000000); + println!("ret:{:?}", ret1); +} + +#[test] +fn test_GeneralIoError() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-client"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_failed(&tx, 1000000000); + println!("ret:{:?}", ret1); +} + From 8c9fae10a7482d003767a227e4896696a6c995da Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Fri, 7 Mar 2025 11:04:55 +0800 Subject: [PATCH 02/18] update case refer --- src/tests/ckb_script_ipc_demo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/ckb_script_ipc_demo.rs b/src/tests/ckb_script_ipc_demo.rs index 54fa97a..db24f16 100644 --- a/src/tests/ckb_script_ipc_demo.rs +++ b/src/tests/ckb_script_ipc_demo.rs @@ -72,7 +72,7 @@ fn test_single_forever_loop() { let input_token_cell = Demo::default(); let mut ct = ContractUtil::new(); - let serve_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-sreve"); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-serve"); let client_contract = ct.deploy_contract("../../demo/build/release/test_single_loop_request-client"); let mut tx = TransactionBuilder::default().build(); From 4851477ab653d930dc3688975fa0e1ab5f25d534 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Fri, 7 Mar 2025 14:11:26 +0800 Subject: [PATCH 03/18] update ipc & ipc common v1.0.1 --- demo/contracts/ckb-script-ipc-demo/Cargo.toml | 4 ++-- demo/contracts/ckb-script-ipc-test/Cargo.toml | 5 ++--- demo/contracts/test_invalid_request/Cargo.toml | 5 ++--- demo/contracts/test_loop_request/Cargo.toml | 5 ++--- demo/contracts/test_single_loop_request/Cargo.toml | 5 ++--- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/demo/contracts/ckb-script-ipc-demo/Cargo.toml b/demo/contracts/ckb-script-ipc-demo/Cargo.toml index 1cf5e3b..aff03b2 100644 --- a/demo/contracts/ckb-script-ipc-demo/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-demo/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +ckb-script-ipc = { version = "1.0.1" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/ckb-script-ipc-test/Cargo.toml b/demo/contracts/ckb-script-ipc-test/Cargo.toml index 2893c74..a288dfb 100644 --- a/demo/contracts/ckb-script-ipc-test/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-test/Cargo.toml @@ -5,10 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } - +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +ckb-script-ipc = { version = "1.0.1" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_invalid_request/Cargo.toml b/demo/contracts/test_invalid_request/Cargo.toml index 8421c05..2c179a4 100644 --- a/demo/contracts/test_invalid_request/Cargo.toml +++ b/demo/contracts/test_invalid_request/Cargo.toml @@ -5,10 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } - +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +ckb-script-ipc = { version = "1.0.1" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_loop_request/Cargo.toml b/demo/contracts/test_loop_request/Cargo.toml index 205adc1..ff4b95a 100644 --- a/demo/contracts/test_loop_request/Cargo.toml +++ b/demo/contracts/test_loop_request/Cargo.toml @@ -5,10 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } - +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +ckb-script-ipc = { version = "1.0.1" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_single_loop_request/Cargo.toml b/demo/contracts/test_single_loop_request/Cargo.toml index 110e53c..8d6f8e1 100644 --- a/demo/contracts/test_single_loop_request/Cargo.toml +++ b/demo/contracts/test_single_loop_request/Cargo.toml @@ -5,10 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359", features = ["enable-logging"] } - +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { git = "https://github.com/XuJiandong/ckb-script-ipc", rev = "10ef359" } +ckb-script-ipc = { version = "1.0.1" } log = "0.4.25" [[bin]] From 1a6c69cf8d2e666361e7b56d188d38c40045b3c0 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Fri, 7 Mar 2025 14:32:46 +0800 Subject: [PATCH 04/18] add large_data case --- demo/Cargo.toml | 3 +- .../test_large_data_handling/.gitignore | 2 + .../test_large_data_handling/Cargo.toml | 19 +++++ .../test_large_data_handling/Makefile | 77 ++++++++++++++++++ .../test_large_data_handling/README.md | 6 ++ .../test_large_data_handling/src/client.rs | 81 +++++++++++++++++++ .../test_large_data_handling/src/def.rs | 8 ++ .../test_large_data_handling/src/error.rs | 6 ++ .../test_large_data_handling/src/server.rs | 73 +++++++++++++++++ src/tests/ckb_script_ipc_demo.rs | 15 ++++ 10 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 demo/contracts/test_large_data_handling/.gitignore create mode 100644 demo/contracts/test_large_data_handling/Cargo.toml create mode 100644 demo/contracts/test_large_data_handling/Makefile create mode 100644 demo/contracts/test_large_data_handling/README.md create mode 100644 demo/contracts/test_large_data_handling/src/client.rs create mode 100644 demo/contracts/test_large_data_handling/src/def.rs create mode 100644 demo/contracts/test_large_data_handling/src/error.rs create mode 100644 demo/contracts/test_large_data_handling/src/server.rs diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 1801b32..1b58168 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -11,7 +11,8 @@ members = [ "tests", "contracts/test_invalid_request", "contracts/test_loop_request", - "contracts/test_single_loop_request" + "contracts/test_single_loop_request", + "contracts/test_large_data_handling" ] [profile.release] diff --git a/demo/contracts/test_large_data_handling/.gitignore b/demo/contracts/test_large_data_handling/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/test_large_data_handling/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/test_large_data_handling/Cargo.toml b/demo/contracts/test_large_data_handling/Cargo.toml new file mode 100644 index 0000000..6102a09 --- /dev/null +++ b/demo/contracts/test_large_data_handling/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "test_large_data_handling" +version = "1.0.0" +edition = "2021" + +[dependencies] +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +serde = { version = "1.0", default-features = false, features = ["derive"] } +ckb-script-ipc = { version = "1.0.1" } +log = "0.4.25" + +[[bin]] +name = "test_large_data_request-client" +path = "src/client.rs" + +[[bin]] +name = "test_large_data_request-serve" +path = "src/server.rs" \ No newline at end of file diff --git a/demo/contracts/test_large_data_handling/Makefile b/demo/contracts/test_large_data_handling/Makefile new file mode 100644 index 0000000..1b811d3 --- /dev/null +++ b/demo/contracts/test_large_data_handling/Makefile @@ -0,0 +1,77 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "test_large_data_request-client" "test_large_data_request-serve" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/test_large_data_handling/README.md b/demo/contracts/test_large_data_handling/README.md new file mode 100644 index 0000000..c267109 --- /dev/null +++ b/demo/contracts/test_large_data_handling/README.md @@ -0,0 +1,6 @@ +# ckb-script-ipc-demo + +A demo for ckb-script-ipc, showcasing inter-process communication (IPC) between +CKB scripts. This demo contains both client and server scripts in a single +codebase for simplicity and ease of understanding. It demonstrates how to use +ckb-script-ipc to enable communication between different CKB scripts. diff --git a/demo/contracts/test_large_data_handling/src/client.rs b/demo/contracts/test_large_data_handling/src/client.rs new file mode 100644 index 0000000..cf5eefe --- /dev/null +++ b/demo/contracts/test_large_data_handling/src/client.rs @@ -0,0 +1,81 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, log::info}; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::def::WorldClient; +use crate::error::Error; +use alloc::string::String; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +// 生成大数据字符串 +fn generate_large_data(size_mb: usize) -> String { + let base_str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let chars_per_mb = 1024 * 1024; // 1MB的字符数 + let total_chars = size_mb * chars_per_mb; + + // 重复基础字符串来创建大数据 + let mut result = String::with_capacity(total_chars); + let mut remaining_chars = total_chars; + + while remaining_chars > 0 { + let chunk_size = if remaining_chars > base_str.len() { + base_str.len() + } else { + remaining_chars + }; + result.push_str(&base_str[..chunk_size]); + remaining_chars = remaining_chars.saturating_sub(chunk_size); + } + + result +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + + // 测试大数据传输 (10MB) + let large_data = generate_large_data(10); + info!("Generated large data size: {} bytes", large_data.len()); + + // 发送大数据 + let ret = client.hello(large_data); + info!("Large data IPC response: {:?}", ret); + + Ok(()) +} diff --git a/demo/contracts/test_large_data_handling/src/def.rs b/demo/contracts/test_large_data_handling/src/def.rs new file mode 100644 index 0000000..23884a2 --- /dev/null +++ b/demo/contracts/test_large_data_handling/src/def.rs @@ -0,0 +1,8 @@ +use alloc::string::String; + +// IPC definition, it can be shared between client and server +#[ckb_script_ipc::service] +pub trait World { + fn hello(name: String) -> Result; + fn get_data() -> usize; +} diff --git a/demo/contracts/test_large_data_handling/src/error.rs b/demo/contracts/test_large_data_handling/src/error.rs new file mode 100644 index 0000000..2c81059 --- /dev/null +++ b/demo/contracts/test_large_data_handling/src/error.rs @@ -0,0 +1,6 @@ +#[repr(i8)] +pub enum Error { + Unknown = 1, + CkbSysError, + ServerError, +} diff --git a/demo/contracts/test_large_data_handling/src/server.rs b/demo/contracts/test_large_data_handling/src/server.rs new file mode 100644 index 0000000..ce1edf9 --- /dev/null +++ b/demo/contracts/test_large_data_handling/src/server.rs @@ -0,0 +1,73 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{log::info}; + +use crate::def::World; +use alloc::{format, string::String}; +use ckb_script_ipc_common::spawn::run_server; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +struct WorldServer { + pub data: usize, +} + +impl WorldServer { + fn new() -> Self { + WorldServer { + data: 2 + } + } +} + +impl World for WorldServer { + // method implementation + fn hello(&mut self, name: String) -> Result { + self.data += 1; + + if name == "error" { + Err(1) + } else if name == "server_error" { + Err(Error::ServerError as u64) + } else if name == "ckb_sys_error" { + Err(Error::CkbSysError as u64) + } else if name == "unknown" { + Err(Error::Unknown as u64) + } else { + // 返回数据大小信息 + Ok(format!("处理成功,数据大小: {} 字节", name.len())) + } + } + + fn get_data(&mut self) -> usize { + return self.data; + } +} + +pub fn server_run() -> Result<(), Error> { + let world = WorldServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} \ No newline at end of file diff --git a/src/tests/ckb_script_ipc_demo.rs b/src/tests/ckb_script_ipc_demo.rs index db24f16..1678e42 100644 --- a/src/tests/ckb_script_ipc_demo.rs +++ b/src/tests/ckb_script_ipc_demo.rs @@ -99,3 +99,18 @@ fn test_GeneralIoError() { println!("ret:{:?}", ret1); } +#[test] +fn test_large_data_handling() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_large_data_request-serve"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_large_data_request-client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_failed(&tx, 1000000000); + println!("ret:{:?}", ret1); +} \ No newline at end of file From c242bca2fb76ecfd731ed279fe113af425acba69 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Fri, 7 Mar 2025 16:26:46 +0800 Subject: [PATCH 05/18] add types for rust --- demo/Cargo.toml | 4 +- demo/contracts/test_serialize/.gitignore | 2 + demo/contracts/test_serialize/Cargo.toml | 19 +++ demo/contracts/test_serialize/Makefile | 77 +++++++++ demo/contracts/test_serialize/README.md | 6 + demo/contracts/test_serialize/src/client.rs | 107 +++++++++++++ demo/contracts/test_serialize/src/def.rs | 33 ++++ demo/contracts/test_serialize/src/error.rs | 6 + demo/contracts/test_serialize/src/server.rs | 94 +++++++++++ .../test_serialize_complex/.gitignore | 2 + .../test_serialize_complex/Cargo.toml | 20 +++ .../contracts/test_serialize_complex/Makefile | 77 +++++++++ .../test_serialize_complex/README.md | 6 + .../test_serialize_complex/src/client.rs | 148 ++++++++++++++++++ .../test_serialize_complex/src/def.rs | 88 +++++++++++ .../test_serialize_complex/src/error.rs | 6 + .../test_serialize_complex/src/server.rs | 105 +++++++++++++ src/tests/ckb_script_ipc_demo.rs | 34 ++++ 18 files changed, 833 insertions(+), 1 deletion(-) create mode 100644 demo/contracts/test_serialize/.gitignore create mode 100644 demo/contracts/test_serialize/Cargo.toml create mode 100644 demo/contracts/test_serialize/Makefile create mode 100644 demo/contracts/test_serialize/README.md create mode 100644 demo/contracts/test_serialize/src/client.rs create mode 100644 demo/contracts/test_serialize/src/def.rs create mode 100644 demo/contracts/test_serialize/src/error.rs create mode 100644 demo/contracts/test_serialize/src/server.rs create mode 100644 demo/contracts/test_serialize_complex/.gitignore create mode 100644 demo/contracts/test_serialize_complex/Cargo.toml create mode 100644 demo/contracts/test_serialize_complex/Makefile create mode 100644 demo/contracts/test_serialize_complex/README.md create mode 100644 demo/contracts/test_serialize_complex/src/client.rs create mode 100644 demo/contracts/test_serialize_complex/src/def.rs create mode 100644 demo/contracts/test_serialize_complex/src/error.rs create mode 100644 demo/contracts/test_serialize_complex/src/server.rs diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 1b58168..9eca3d0 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -12,7 +12,9 @@ members = [ "contracts/test_invalid_request", "contracts/test_loop_request", "contracts/test_single_loop_request", - "contracts/test_large_data_handling" + "contracts/test_large_data_handling", + "contracts/test_serialize", + "contracts/test_serialize_complex" ] [profile.release] diff --git a/demo/contracts/test_serialize/.gitignore b/demo/contracts/test_serialize/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/test_serialize/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/test_serialize/Cargo.toml b/demo/contracts/test_serialize/Cargo.toml new file mode 100644 index 0000000..c426f50 --- /dev/null +++ b/demo/contracts/test_serialize/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "test_serialize" +version = "1.0.0" +edition = "2021" + +[dependencies] +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +serde = { version = "1.0", default-features = false, features = ["derive"] } +ckb-script-ipc = { version = "1.0.1" } +log = "0.4.25" + +[[bin]] +name = "test_serialize-client" +path = "src/client.rs" + +[[bin]] +name = "test_serialize-serve" +path = "src/server.rs" \ No newline at end of file diff --git a/demo/contracts/test_serialize/Makefile b/demo/contracts/test_serialize/Makefile new file mode 100644 index 0000000..ae3a02e --- /dev/null +++ b/demo/contracts/test_serialize/Makefile @@ -0,0 +1,77 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "test_serialize-client" "test_serialize-serve" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/test_serialize/README.md b/demo/contracts/test_serialize/README.md new file mode 100644 index 0000000..c267109 --- /dev/null +++ b/demo/contracts/test_serialize/README.md @@ -0,0 +1,6 @@ +# ckb-script-ipc-demo + +A demo for ckb-script-ipc, showcasing inter-process communication (IPC) between +CKB scripts. This demo contains both client and server scripts in a single +codebase for simplicity and ease of understanding. It demonstrates how to use +ckb-script-ipc to enable communication between different CKB scripts. diff --git a/demo/contracts/test_serialize/src/client.rs b/demo/contracts/test_serialize/src/client.rs new file mode 100644 index 0000000..d89fe31 --- /dev/null +++ b/demo/contracts/test_serialize/src/client.rs @@ -0,0 +1,107 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, log::info}; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::def::WorldClient; +use crate::error::Error; +use alloc::string::String; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +// 生成大数据字符串 +fn generate_large_data(size_mb: usize) -> String { + let base_str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let chars_per_mb = 1024 * 1024; // 1MB的字符数 + let total_chars = size_mb * chars_per_mb; + + // 重复基础字符串来创建大数据 + let mut result = String::with_capacity(total_chars); + let mut remaining_chars = total_chars; + + while remaining_chars > 0 { + let chunk_size = if remaining_chars > base_str.len() { + base_str.len() + } else { + remaining_chars + }; + result.push_str(&base_str[..chunk_size]); + remaining_chars = remaining_chars.saturating_sub(chunk_size); + } + + result +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + + // 测试整数类型 + info!("测试整数类型"); + info!("i8: {:?}", client.test_i8(-8)); + info!("i16: {:?}", client.test_i16(-16)); + info!("i32: {:?}", client.test_i32(-32)); + info!("i64: {:?}", client.test_i64(-64)); + info!("i128: {:?}", client.test_i128(-128)); + + info!("u8: {:?}", client.test_u8(8)); + info!("u16: {:?}", client.test_u16(16)); + info!("u32: {:?}", client.test_u32(32)); + info!("u64: {:?}", client.test_u64(64)); + info!("u128: {:?}", client.test_u128(128)); + + // 测试浮点类型 + info!("测试浮点类型"); + info!("f32: {:?}", client.test_f32(3.14)); + info!("f64: {:?}", client.test_f64(6.28)); + + // 测试布尔类型 + info!("测试布尔类型"); + info!("bool true: {:?}", client.test_bool(true)); + info!("bool false: {:?}", client.test_bool(false)); + + // 测试字符类型 + info!("测试字符类型"); + info!("char: {:?}", client.test_char('你')); + + // // 测试字符串类型(原有功能) + // let large_data = generate_large_data(1); // 使用1MB数据测试 + // info!("测试字符串类型,数据大小: {} bytes", large_data.len()); + // let ret = client.hello(large_data); + // info!("字符串响应: {:?}", ret); + + Ok(()) +} diff --git a/demo/contracts/test_serialize/src/def.rs b/demo/contracts/test_serialize/src/def.rs new file mode 100644 index 0000000..985e89b --- /dev/null +++ b/demo/contracts/test_serialize/src/def.rs @@ -0,0 +1,33 @@ +use alloc::string::String; + +// IPC definition, it can be shared between client and server +#[ckb_script_ipc::service] +pub trait World { + // 测试整数类型 + fn test_i8(val: i8) -> Result; + fn test_i16(val: i16) -> Result; + fn test_i32(val: i32) -> Result; + fn test_i64(val: i64) -> Result; + fn test_i128(val: i128) -> Result; + + fn test_u8(val: u8) -> Result; + fn test_u16(val: u16) -> Result; + fn test_u32(val: u32) -> Result; + fn test_u64(val: u64) -> Result; + fn test_u128(val: u128) -> Result; + + // 测试浮点类型 + fn test_f32(val: f32) -> Result; + fn test_f64(val: f64) -> Result; + + // 测试布尔类型 + fn test_bool(val: bool) -> Result; + + // 测试字符类型 + fn test_char(val: char) -> Result; + + // 原有方法保持不变 + fn hello(name: String) -> Result; + fn get_data() -> usize; +} + diff --git a/demo/contracts/test_serialize/src/error.rs b/demo/contracts/test_serialize/src/error.rs new file mode 100644 index 0000000..2c81059 --- /dev/null +++ b/demo/contracts/test_serialize/src/error.rs @@ -0,0 +1,6 @@ +#[repr(i8)] +pub enum Error { + Unknown = 1, + CkbSysError, + ServerError, +} diff --git a/demo/contracts/test_serialize/src/server.rs b/demo/contracts/test_serialize/src/server.rs new file mode 100644 index 0000000..b8fdea4 --- /dev/null +++ b/demo/contracts/test_serialize/src/server.rs @@ -0,0 +1,94 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{log::info}; + +use crate::def::World; +use alloc::{format, string::String}; +use ckb_script_ipc_common::spawn::run_server; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +struct WorldServer { + pub data: usize, +} + +impl WorldServer { + fn new() -> Self { + WorldServer { + data: 2 + } + } +} + +impl World for WorldServer { + // 整数类型测试实现 + fn test_i8(&mut self, val: i8) -> Result { Ok(val) } + fn test_i16(&mut self, val: i16) -> Result { Ok(val) } + fn test_i32(&mut self, val: i32) -> Result { Ok(val) } + fn test_i64(&mut self, val: i64) -> Result { Ok(val) } + fn test_i128(&mut self, val: i128) -> Result { Ok(val) } + + fn test_u8(&mut self, val: u8) -> Result { Ok(val) } + fn test_u16(&mut self, val: u16) -> Result { Ok(val) } + fn test_u32(&mut self, val: u32) -> Result { Ok(val) } + fn test_u64(&mut self, val: u64) -> Result { Ok(val) } + fn test_u128(&mut self, val: u128) -> Result { Ok(val) } + + // 浮点类型测试实现 + fn test_f32(&mut self, val: f32) -> Result { Ok(val) } + fn test_f64(&mut self, val: f64) -> Result { Ok(val) } + + // 布尔类型测试实现 + fn test_bool(&mut self, val: bool) -> Result { Ok(val) } + + // 字符类型测试实现 + fn test_char(&mut self, val: char) -> Result { Ok(val) } + + // 原有方法实现 + fn hello(&mut self, name: String) -> Result { + self.data += 1; + if name == "error" { + Err(1) + } else if name == "server_error" { + Err(Error::ServerError as u64) + } else if name == "ckb_sys_error" { + Err(Error::CkbSysError as u64) + } else if name == "unknown" { + Err(Error::Unknown as u64) + } else { + Ok(format!("处理成功,数据大小: {} 字节", name.len())) + } + } + + fn get_data(&mut self) -> usize { + return self.data; + } +} + +pub fn server_run() -> Result<(), Error> { + let world = WorldServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} \ No newline at end of file diff --git a/demo/contracts/test_serialize_complex/.gitignore b/demo/contracts/test_serialize_complex/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/test_serialize_complex/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/test_serialize_complex/Cargo.toml b/demo/contracts/test_serialize_complex/Cargo.toml new file mode 100644 index 0000000..3beb47d --- /dev/null +++ b/demo/contracts/test_serialize_complex/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test_serialize_complex" +version = "1.0.0" +edition = "2021" + +[dependencies] +ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +serde = { version = "1.0", default-features = false, features = ["derive", "rc", "alloc"] } +serde_with = { version = "3.6.1", default-features = false, features = ["alloc", "macros"] } +ckb-script-ipc = { version = "1.0.1" } +log = "0.4.25" + +[[bin]] +name = "test_serialize-complex_client" +path = "src/client.rs" + +[[bin]] +name = "test_serialize-complex_serve" +path = "src/server.rs" \ No newline at end of file diff --git a/demo/contracts/test_serialize_complex/Makefile b/demo/contracts/test_serialize_complex/Makefile new file mode 100644 index 0000000..bdcc085 --- /dev/null +++ b/demo/contracts/test_serialize_complex/Makefile @@ -0,0 +1,77 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := --cfg debug_assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "test_serialize-complex_client" "test_serialize-complex_serve" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/test_serialize_complex/README.md b/demo/contracts/test_serialize_complex/README.md new file mode 100644 index 0000000..c267109 --- /dev/null +++ b/demo/contracts/test_serialize_complex/README.md @@ -0,0 +1,6 @@ +# ckb-script-ipc-demo + +A demo for ckb-script-ipc, showcasing inter-process communication (IPC) between +CKB scripts. This demo contains both client and server scripts in a single +codebase for simplicity and ease of understanding. It demonstrates how to use +ckb-script-ipc to enable communication between different CKB scripts. diff --git a/demo/contracts/test_serialize_complex/src/client.rs b/demo/contracts/test_serialize_complex/src/client.rs new file mode 100644 index 0000000..6040eef --- /dev/null +++ b/demo/contracts/test_serialize_complex/src/client.rs @@ -0,0 +1,148 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use alloc::vec; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, log::info}; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::def::{WorldClient, Point, Color}; +use crate::error::Error; +use alloc::string::String; +use alloc::vec::Vec; +use alloc::collections::BTreeMap; +use alloc::rc::Rc; +use alloc::sync::Arc; +use alloc::boxed::Box; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + + // 测试整数类型 + info!("测试整数类型"); + info!("i8: {:?}", client.test_i8(-8)); + info!("i16: {:?}", client.test_i16(-16)); + info!("i32: {:?}", client.test_i32(-32)); + info!("i64: {:?}", client.test_i64(-64)); + info!("i128: {:?}", client.test_i128(-128)); + + info!("u8: {:?}", client.test_u8(8)); + info!("u16: {:?}", client.test_u16(16)); + info!("u32: {:?}", client.test_u32(32)); + info!("u64: {:?}", client.test_u64(64)); + info!("u128: {:?}", client.test_u128(128)); + + // 测试浮点类型 + info!("测试浮点类型"); + info!("f32: {:?}", client.test_f32(3.14)); + info!("f64: {:?}", client.test_f64(6.28)); + + // 测试布尔类型 + info!("测试布尔类型"); + info!("bool true: {:?}", client.test_bool(true)); + info!("bool false: {:?}", client.test_bool(false)); + + // 测试字符类型 + info!("测试字符类型"); + info!("char: {:?}", client.test_char('你')); + + // 测试结构体 + info!("测试结构体"); + let point = Point { x: 10, y: 20 }; + info!("struct Point: {:?}", client.test_struct(point)); + + // 测试枚举 + info!("测试枚举"); + info!("enum Color::Red: {:?}", client.test_enum(Color::Red)); + info!("enum Color::Custom: {:?}", client.test_enum(Color::Custom(255, 128, 0))); + + // 测试元组 + info!("测试元组"); + let tuple_data = (42, String::from("test"), true); + info!("tuple: {:?}", client.test_tuple(tuple_data)); + + // 测试固定大小数组 + info!("测试固定大小数组"); + let array_data = [1, 2, 3, 4, 5]; + info!("array: {:?}", client.test_array(array_data)); + + // 测试向量 + info!("测试向量"); + let vec_data = vec![1, 2, 3, 4, 5]; + info!("vector: {:?}", client.test_vec(vec_data)); + + // 测试Option + info!("测试Option"); + info!("Some: {:?}", client.test_option(Some(String::from("test")))); + info!("None: {:?}", client.test_option(None)); + + // 测试嵌套结构 + info!("测试嵌套结构"); + let nested_data = vec![Some(Point { x: 1, y: 2 }), None, Some(Point { x: 3, y: 4 })]; + info!("nested: {:?}", client.test_nested(nested_data)); + + // 测试映射类型 + info!("测试映射类型"); + let mut map_data = BTreeMap::new(); + map_data.insert(String::from("key1"), 1); + map_data.insert(String::from("key2"), 2); + info!("map: {:?}", client.test_map(map_data)); + + // 测试Box智能指针 + info!("测试Box智能指针"); + let boxed_int = Box::new(42); + info!("boxed int: {:?}", client.test_box(boxed_int)); + let boxed_point = Box::new(Point { x: 100, y: 200 }); + info!("boxed point: {:?}", client.test_box_struct(boxed_point)); + + // 测试Rc引用计数智能指针 + info!("测试Rc引用计数智能指针"); + let rc_string = Rc::new(String::from("test rc")); + info!("rc string: {:?}", client.test_rc(rc_string)); + + // 测试Arc原子引用计数智能指针 + info!("测试Arc原子引用计数智能指针"); + let arc_vec = Arc::new(vec![1, 2, 3, 4, 5]); + info!("arc vector: {:?}", client.test_arc(arc_vec)); + + // 测试原有功能 + info!("测试原有功能"); + info!("hello: {:?}", client.hello(String::from("world"))); + info!("get_data: {}", client.get_data()); + + Ok(()) +} diff --git a/demo/contracts/test_serialize_complex/src/def.rs b/demo/contracts/test_serialize_complex/src/def.rs new file mode 100644 index 0000000..6c53c32 --- /dev/null +++ b/demo/contracts/test_serialize_complex/src/def.rs @@ -0,0 +1,88 @@ +use alloc::string::String; +use alloc::vec::Vec; +use alloc::collections::BTreeMap; +use alloc::rc::Rc; +use alloc::sync::Arc; +use alloc::boxed::Box; +use serde_with::serde_as; + +// 测试用的复杂类型定义 +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Point { + pub x: i32, + pub y: i32, +} + +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum Color { + Red, + Green, + Blue, + Custom(u8, u8, u8), +} + +// IPC definition, it can be shared between client and server +#[ckb_script_ipc::service] +pub trait World { + // 测试整数类型 + fn test_i8(val: i8) -> Result; + fn test_i16(val: i16) -> Result; + fn test_i32(val: i32) -> Result; + fn test_i64(val: i64) -> Result; + fn test_i128(val: i128) -> Result; + + fn test_u8(val: u8) -> Result; + fn test_u16(val: u16) -> Result; + fn test_u32(val: u32) -> Result; + fn test_u64(val: u64) -> Result; + fn test_u128(val: u128) -> Result; + + // 测试浮点类型 + fn test_f32(val: f32) -> Result; + fn test_f64(val: f64) -> Result; + + // 测试布尔类型 + fn test_bool(val: bool) -> Result; + + // 测试字符类型 + fn test_char(val: char) -> Result; + + // 测试结构体 + fn test_struct(point: Point) -> Result; + + // 测试枚举 + fn test_enum(color: Color) -> Result; + + // 测试元组 + fn test_tuple(data: (i32, String, bool)) -> Result<(i32, String, bool), u64>; + + // 测试固定大小数组 + fn test_array(data: [i32; 5]) -> Result<[i32; 5], u64>; + + // 测试向量 + fn test_vec(data: Vec) -> Result, u64>; + + // 测试Option + fn test_option(data: Option) -> Result, u64>; + + // 测试嵌套结构 + fn test_nested(data: Vec>) -> Result>, u64>; + + // 测试映射类型 + fn test_map(data: BTreeMap) -> Result, u64>; + + // 测试Box智能指针 + fn test_box(data: Box) -> Result, u64>; + fn test_box_struct(data: Box) -> Result, u64>; + + // 测试Rc引用计数智能指针 + fn test_rc(data: Rc) -> Result, u64>; + + // 测试Arc原子引用计数智能指针 + fn test_arc(data: Arc>) -> Result>, u64>; + + // 原有方法保持不变 + fn hello(name: String) -> Result; + fn get_data() -> usize; +} + diff --git a/demo/contracts/test_serialize_complex/src/error.rs b/demo/contracts/test_serialize_complex/src/error.rs new file mode 100644 index 0000000..2c81059 --- /dev/null +++ b/demo/contracts/test_serialize_complex/src/error.rs @@ -0,0 +1,6 @@ +#[repr(i8)] +pub enum Error { + Unknown = 1, + CkbSysError, + ServerError, +} diff --git a/demo/contracts/test_serialize_complex/src/server.rs b/demo/contracts/test_serialize_complex/src/server.rs new file mode 100644 index 0000000..d891f2e --- /dev/null +++ b/demo/contracts/test_serialize_complex/src/server.rs @@ -0,0 +1,105 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{log::info}; +use crate::def::{World, Point, Color}; +use alloc::{format, string::String, vec::Vec, collections::BTreeMap, rc::Rc, sync::Arc, boxed::Box}; +use ckb_script_ipc_common::spawn::run_server; + +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use crate::error::Error; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +struct WorldServer { + pub data: usize, +} + +impl WorldServer { + fn new() -> Self { + WorldServer { + data: 2 + } + } +} + +impl World for WorldServer { + fn test_i8(&mut self, val: i8) -> Result { Ok(val) } + fn test_i16(&mut self, val: i16) -> Result { Ok(val) } + fn test_i32(&mut self, val: i32) -> Result { Ok(val) } + fn test_i64(&mut self, val: i64) -> Result { Ok(val) } + fn test_i128(&mut self, val: i128) -> Result { Ok(val) } + + fn test_u8(&mut self, val: u8) -> Result { Ok(val) } + fn test_u16(&mut self, val: u16) -> Result { Ok(val) } + fn test_u32(&mut self, val: u32) -> Result { Ok(val) } + fn test_u64(&mut self, val: u64) -> Result { Ok(val) } + fn test_u128(&mut self, val: u128) -> Result { Ok(val) } + + fn test_f32(&mut self, val: f32) -> Result { Ok(val) } + fn test_f64(&mut self, val: f64) -> Result { Ok(val) } + + fn test_bool(&mut self, val: bool) -> Result { Ok(val) } + + fn test_char(&mut self, val: char) -> Result { Ok(val) } + + fn test_struct(&mut self, point: Point) -> Result { Ok(point) } + + fn test_enum(&mut self, color: Color) -> Result { Ok(color) } + + fn test_tuple(&mut self, data: (i32, String, bool)) -> Result<(i32, String, bool), u64> { Ok(data) } + + fn test_array(&mut self, data: [i32; 5]) -> Result<[i32; 5], u64> { Ok(data) } + + fn test_vec(&mut self, data: Vec) -> Result, u64> { Ok(data) } + + fn test_option(&mut self, data: Option) -> Result, u64> { Ok(data) } + + fn test_nested(&mut self, data: Vec>) -> Result>, u64> { Ok(data) } + + fn test_map(&mut self, data: BTreeMap) -> Result, u64> { Ok(data) } + + fn test_box(&mut self, data: Box) -> Result, u64> { Ok(data) } + fn test_box_struct(&mut self, data: Box) -> Result, u64> { Ok(data) } + + fn test_rc(&mut self, data: Rc) -> Result, u64> { Ok(data) } + + fn test_arc(&mut self, data: Arc>) -> Result>, u64> { Ok(data) } + + fn hello(&mut self, name: String) -> Result { + self.data += 1; + if name == "error" { + Err(1) + } else { + Ok(format!("hello, {}", name)) + } + } + + fn get_data(&mut self) -> usize { + return self.data; + } +} + +pub fn server_run() -> Result<(), Error> { + let world = WorldServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} \ No newline at end of file diff --git a/src/tests/ckb_script_ipc_demo.rs b/src/tests/ckb_script_ipc_demo.rs index 1678e42..c62df06 100644 --- a/src/tests/ckb_script_ipc_demo.rs +++ b/src/tests/ckb_script_ipc_demo.rs @@ -113,4 +113,38 @@ fn test_large_data_handling() { tx = ct.context.complete_tx(tx); let ret1 = ct.context.should_be_failed(&tx, 1000000000); println!("ret:{:?}", ret1); +} + + +#[test] +fn test_serialization_simple_types() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_serialize-serve"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_serialize-client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_passed(&tx, 1000000000); + println!("ret:{:?}", ret1); +} + + +#[test] +fn test_serialization_complex_types() { + + let input_token_cell = Demo::default(); + let mut ct = ContractUtil::new(); + let serve_contract = ct.deploy_contract("../../demo/build/release/test_serialize-complex_serve"); + let client_contract = ct.deploy_contract("../../demo/build/release/test_serialize-complex_client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, client_contract.clone(), None, &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx,&serve_contract); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_passed(&tx, 1000000000); + println!("ret:{:?}", ret1); } \ No newline at end of file From b0ee3a3a49868302f4f6e1b582ddc326c2782b78 Mon Sep 17 00:00:00 2001 From: gpBlockchain <744158715@qq.com> Date: Fri, 7 Mar 2025 22:00:09 +0800 Subject: [PATCH 06/18] add ipc test --- Cargo.toml | 4 +- demo/Cargo.toml | 1 + demo/contracts/ckb-script-ipc-test/Cargo.toml | 42 ++++- demo/contracts/ckb-script-ipc-test/Makefile | 2 +- .../src/boundary_client.rs | 93 +++++++++++ .../src/boundary_server.rs | 69 ++++++++ .../src/chain_error_propagation_client.rs | 60 +++++++ .../ckb-script-ipc-test/src/client.rs | 19 ++- .../src/complex_chain_call_client.rs | 157 ++++++++++++++++++ demo/contracts/ckb-script-ipc-test/src/def.rs | 114 ++++++++++++- .../src/large_data_client.rs | 91 ++++++++++ .../src/large_data_client_2.rs | 87 ++++++++++ .../src/large_data_server.rs | 74 +++++++++ .../ckb-script-ipc-test/src/linked_client.rs | 85 ++++++++++ .../ckb-script-ipc-test/src/linked_server.rs | 120 +++++++++++++ .../ckb-script-ipc-test/src/server.rs | 10 +- demo/contracts/exec-contract/.gitignore | 2 + demo/contracts/exec-contract/Cargo.toml | 15 ++ demo/contracts/exec-contract/Makefile | 80 +++++++++ demo/contracts/exec-contract/README.md | 7 + .../exec-contract/src/exec_arg_length.rs | 119 +++++++++++++ src/tests/ckb_script_ipc_boundary.rs | 23 +++ src/tests/ckb_script_ipc_larger.rs | 59 +++++++ src/tests/ckb_script_linked_call.rs | 72 ++++++++ src/tests/exec.rs | 18 ++ src/tests/mod.rs | 4 + 26 files changed, 1406 insertions(+), 21 deletions(-) create mode 100644 demo/contracts/ckb-script-ipc-test/src/boundary_client.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/boundary_server.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/chain_error_propagation_client.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/complex_chain_call_client.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/large_data_client.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/large_data_client_2.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/large_data_server.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/linked_client.rs create mode 100644 demo/contracts/ckb-script-ipc-test/src/linked_server.rs create mode 100644 demo/contracts/exec-contract/.gitignore create mode 100644 demo/contracts/exec-contract/Cargo.toml create mode 100644 demo/contracts/exec-contract/Makefile create mode 100644 demo/contracts/exec-contract/README.md create mode 100644 demo/contracts/exec-contract/src/exec_arg_length.rs create mode 100644 src/tests/ckb_script_ipc_boundary.rs create mode 100644 src/tests/ckb_script_ipc_larger.rs create mode 100644 src/tests/ckb_script_linked_call.rs create mode 100644 src/tests/exec.rs diff --git a/Cargo.toml b/Cargo.toml index c2a29d3..f47e02a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] -ckb-testtool = "0.14.1" +ckb-testtool = "0.15.0" serde_json = "1.0" bytes = "1.5.0" serde = { version = "1.0.209", features = ["derive"] } @@ -16,7 +16,7 @@ hex = "0.4.3" syn = "2.0.98" [dev-dependencies] -ckb-std = "0.16.4" +ckb-std = "0.17.0" bitcoin = { version = "0.31", features = ["serde"] } log = "0.4" env_logger = "0.11" diff --git a/demo/Cargo.toml b/demo/Cargo.toml index b26b5e5..83ac4fe 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -8,6 +8,7 @@ members = [ "contracts/first-contract", "contracts/ckb-script-ipc-demo", "contracts/ckb-script-ipc-test", + "contracts/exec-contract", "tests", ] diff --git a/demo/contracts/ckb-script-ipc-test/Cargo.toml b/demo/contracts/ckb-script-ipc-test/Cargo.toml index ba4ad2a..deacbd7 100644 --- a/demo/contracts/ckb-script-ipc-test/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-test/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" edition = "2021" [dependencies] -ckb-std = { version = "0.16", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} +ckb-std = { version = "0.16", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"] } #ckb-script-ipc-common = { path = "../../crates/ckb-script-ipc-common" } ckb-script-ipc-common = "1.0.0" @@ -18,4 +18,42 @@ path = "src/client.rs" [[bin]] name = "ckb-script-ipc-test-serve" -path = "src/server.rs" \ No newline at end of file +path = "src/server.rs" + +[[bin]] +name = "large_data_server" +path = "src/large_data_server.rs" + +[[bin]] +name = "large_data_client" +path = "src/large_data_client.rs" + +[[bin]] +name = "large_data_client2" +path = "src/large_data_client_2.rs" + +[[bin]] +name = "boundary_server" +path = "src/boundary_server.rs" + +[[bin]] +name = "boundary_client" +path = "src/boundary_client.rs" + +[[bin]] +name = "linked_server" +path = "src/linked_server.rs" + + +[[bin]] +name = "linked_client" +path = "src/linked_client.rs" + +[[bin]] +name = "complex_chain_call_client" +path = "src/complex_chain_call_client.rs" + +[[bin]] +name = "chain_error_propagation_client" +path = "src/chain_error_propagation_client.rs" + diff --git a/demo/contracts/ckb-script-ipc-test/Makefile b/demo/contracts/ckb-script-ipc-test/Makefile index c82371e..5681ab0 100644 --- a/demo/contracts/ckb-script-ipc-test/Makefile +++ b/demo/contracts/ckb-script-ipc-test/Makefile @@ -25,7 +25,7 @@ BUILD_DIR := # likely match the crate name, which is also the name of the final binary. # However if this is not the case, you can tweak this variable. As the name hints, # more than one binary is supported here. -BINARIES := "ckb-script-ipc-test-serve" "ckb-script-ipc-test-client" +BINARIES := "ckb-script-ipc-test-serve" "ckb-script-ipc-test-client" "large_data_server" "large_data_client" "large_data_client2" "boundary_server" "boundary_client" "linked_server" "linked_client" "complex_chain_call_client" "chain_error_propagation_client" ifeq (release,$(MODE)) MODE_ARGS := --release diff --git a/demo/contracts/ckb-script-ipc-test/src/boundary_client.rs b/demo/contracts/ckb-script-ipc-test/src/boundary_client.rs new file mode 100644 index 0000000..5be5d98 --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/boundary_client.rs @@ -0,0 +1,93 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod error; +use ckb_std::debug; + +use crate::error::Error; +use alloc::ffi::CString; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use ckb_script_ipc_common::spawn::spawn_server; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::high_level::{load_cell_data, QueryIter}; +use ckb_std::logger; +use ckb_std::syscalls::current_cycles; +use ckb_std::{ckb_constants::Source, log::info}; + +pub mod def; +use crate::def::{BoundaryStruct, TestBoundaryClient}; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = TestBoundaryClient::new(read_pipe, write_pipe); + + let data_length = collect_outputs_amount().unwrap(); + + // invoke + let ret = client.test(vec![ + BoundaryStruct::min_value(), + BoundaryStruct::max_value(), + ]); + info!("IPC response: {:?}", ret); + + Ok(()) +} +const UDT_LEN: usize = 16; + +fn generate_rand_str(len: usize) -> String { + let mut s = String::new(); + for _ in 0..len { + s.push((current_cycles() as u8 % 26 + 97) as char); + } + s +} + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut buf = [0u8; UDT_LEN]; + debug!( + "QueryIter:{:?}", + QueryIter::new(load_cell_data, Source::GroupOutput).count() + ); + let udt_list = QueryIter::new(load_cell_data, Source::GroupOutput) + .map(|data| { + if data.len() == UDT_LEN { + buf.copy_from_slice(&data); + // u128 is 16 bytes + Ok(u128::from_le_bytes(buf)) + } else { + Err(9) + } + }) + .collect::, u8>>()?; + Ok(udt_list.into_iter().sum::()) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/boundary_server.rs b/demo/contracts/ckb-script-ipc-test/src/boundary_server.rs new file mode 100644 index 0000000..07465f8 --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/boundary_server.rs @@ -0,0 +1,69 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::vec::Vec; + +use crate::def::{BoundaryStruct, TestBoundary}; +use ckb_script_ipc_common::spawn::run_server; + +use crate::error::Error; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub struct BoundaryServer { + pub data: usize, +} + +impl BoundaryServer { + fn new() -> Self { + BoundaryServer { data: 2 } + } +} + +impl TestBoundary for BoundaryServer { + // method implementation + fn test(&mut self, vec: Vec) -> Vec { + self.data += 1; + return vec; + } +} + +// impl World for LargeDataServer { +// // method implementation +// fn hello(&mut self, name: String) -> Result { +// self.data += 1; +// if name == "error" { +// Err(1) +// } else { +// Ok(format!("hello, {}", name)) +// } +// } +// fn get_data(&mut self) -> usize { +// return self.data; +// } +// } + +pub fn server_run() -> Result<(), Error> { + let world = BoundaryServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/chain_error_propagation_client.rs b/demo/contracts/ckb-script-ipc-test/src/chain_error_propagation_client.rs new file mode 100644 index 0000000..445e06a --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/chain_error_propagation_client.rs @@ -0,0 +1,60 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; +use crate::def::WorldClient; + +pub mod def; +pub mod error; + +use alloc::ffi::CString; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::{ckb_constants::Source, debug, log::info}; + +use crate::error::Error; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + info!("call hello"); + + // invoke + // let ret = client.hello("world \0\n\\n\\\\\''''' ///@@".into()).unwrap(); + let ret = match client.hello("world".into()) { + Ok(ok) => ok, + Err(err) => { + debug!("error: {:?}", err); + return Err(Error::CkbSysError); + } + }; + + Ok(()) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/client.rs b/demo/contracts/ckb-script-ipc-test/src/client.rs index 62b919a..a084877 100644 --- a/demo/contracts/ckb-script-ipc-test/src/client.rs +++ b/demo/contracts/ckb-script-ipc-test/src/client.rs @@ -3,6 +3,7 @@ #[cfg(test)] extern crate alloc; +use crate::def::WorldClient; pub mod def; pub mod error; @@ -11,11 +12,10 @@ use alloc::ffi::CString; use ckb_script_ipc_common::spawn::spawn_server; use ckb_std::{ckb_constants::Source, log::info}; +use crate::error::Error; #[cfg(not(test))] use ckb_std::default_alloc; use ckb_std::logger; -use crate::def::WorldClient; -use crate::error::Error; #[cfg(not(test))] ckb_std::entry!(program_entry); @@ -30,7 +30,6 @@ pub fn program_entry() -> i8 { } } - pub fn client_run() -> Result<(), Error> { info!("client run started"); @@ -41,30 +40,32 @@ pub fn client_run() -> Result<(), Error> { Source::CellDep, &[CString::new("demo").unwrap().as_ref()], ) - .map_err(|_| Error::CkbSysError)?; + .map_err(|_| Error::CkbSysError)?; let (read_pipe1, write_pipe1) = spawn_server( 0, Source::CellDep, &[CString::new("demo").unwrap().as_ref()], ) - .map_err(|_| Error::CkbSysError)?; + .map_err(|_| Error::CkbSysError)?; // new client let mut client = WorldClient::new(read_pipe, write_pipe); let mut client2 = WorldClient::new(read_pipe1, write_pipe1); // invoke - let ret = client.hello("world \0\n\\n\\\\\''''' ///@@".into()).unwrap(); + let ret = client + .hello("world \0\n\\n\\\\\''''' ///@@".into()) + .unwrap(); info!("IPC response: {:?}", ret); let data = client.get_data(); - info!("data:{:?}",data); + info!("data:{:?}", data); // invoke again, should return error let ret = client.hello("error".into()); info!("IPC response: {:?}", ret); let data = client.get_data(); - info!("data:{:?}",data); + info!("data:{:?}", data); let data = client2.get_data(); - info!("data:{:?}",data); + info!("data:{:?}", data); Ok(()) } diff --git a/demo/contracts/ckb-script-ipc-test/src/complex_chain_call_client.rs b/demo/contracts/ckb-script-ipc-test/src/complex_chain_call_client.rs new file mode 100644 index 0000000..55d21d6 --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/complex_chain_call_client.rs @@ -0,0 +1,157 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod error; +use ckb_std::debug; + +use crate::error::Error; +use alloc::ffi::CString; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use ckb_script_ipc_common::spawn::spawn_server; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::high_level::{load_cell_data, QueryIter}; +use ckb_std::logger; +use ckb_std::syscalls::current_cycles; +use ckb_std::{ckb_constants::Source, log::info}; + +pub mod def; +use crate::def::{ + BoundaryStruct, TestBoundaryClient, TestLargeDataClient, TestLinkedCallClient, WorldClient, +}; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // linked_server.rs + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + let mut linked_client = TestLinkedCallClient::new(read_pipe, write_pipe); + + // boundary_server.rs + let (read_pipe, write_pipe) = spawn_server( + 1, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + // new client + let mut boundary_client = TestBoundaryClient::new(read_pipe, write_pipe); + + // large_data_server.rs + let (read_pipe, write_pipe) = spawn_server( + 2, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + let mut large_client = TestLargeDataClient::new(read_pipe, write_pipe); + + let data_length = collect_outputs_amount().unwrap(); + + // server.rs + let (read_pipe, write_pipe) = spawn_server( + 3, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + let (read_pipe1, write_pipe1) = spawn_server( + 3, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + // new client + let mut client = WorldClient::new(read_pipe, write_pipe); + let mut client2 = WorldClient::new(read_pipe1, write_pipe1); + let ret = linked_client.test_linked_call_self(data_length as usize); + // invoke + let expected_ret = (1 + data_length) * data_length / 2; + info!("IPC response: {:?}", ret); + assert_eq!(ret, expected_ret as usize); + for i in 0..100 { + // invoke + let ret = client + .hello("world \0\n\\n\\\\\''''' ///@@".into()) + .unwrap(); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}", data); + // invoke again, should return error + let ret = client.hello("error".into()); + info!("IPC response: {:?}", ret); + let data = client.get_data(); + info!("data:{:?}", data); + let data = client2.get_data(); + info!("data:{:?}", data); + + let data_length = collect_outputs_amount().unwrap(); + let ret = boundary_client.test(vec![ + BoundaryStruct::min_value(), + BoundaryStruct::max_value(), + ]); + info!("IPC response: {:?}", ret); + assert_eq!(ret.len(), 2); + + let test_str = generate_rand_str(data_length as usize); + let ret = large_client.test_large_data_handling(test_str.clone().into()); + info!("IPC response: {:?}", ret); + } + Ok(()) +} +const UDT_LEN: usize = 16; + +fn generate_rand_str(len: usize) -> String { + let mut s = String::new(); + for _ in 0..len { + s.push((current_cycles() as u8 % 26 + 97) as char); + } + s +} + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut buf = [0u8; UDT_LEN]; + debug!( + "QueryIter:{:?}", + QueryIter::new(load_cell_data, Source::GroupOutput).count() + ); + let udt_list = QueryIter::new(load_cell_data, Source::GroupOutput) + .map(|data| { + if data.len() == UDT_LEN { + buf.copy_from_slice(&data); + // u128 is 16 bytes + Ok(u128::from_le_bytes(buf)) + } else { + Err(9) + } + }) + .collect::, u8>>()?; + Ok(udt_list.into_iter().sum::()) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/def.rs b/demo/contracts/ckb-script-ipc-test/src/def.rs index 23884a2..43ffd44 100644 --- a/demo/contracts/ckb-script-ipc-test/src/def.rs +++ b/demo/contracts/ckb-script-ipc-test/src/def.rs @@ -1,4 +1,8 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use ckb_std::ckb_types::bytes::Bytes; +use core::f64; +use serde::{Deserialize, Serialize}; // IPC definition, it can be shared between client and server #[ckb_script_ipc::service] @@ -6,3 +10,111 @@ pub trait World { fn hello(name: String) -> Result; fn get_data() -> usize; } + +#[ckb_script_ipc::service] +pub trait TestLargeData { + fn test_large_data_handling(data: String) -> String; + fn test_large_data_handling2(data: Vec) -> Vec; +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct BoundaryStruct { + pub usize_data: usize, + pub u128_data: u128, + pub u64_data: u64, + pub u32_data: u32, + pub u16_data: u16, + pub u8_data: u8, + pub isize_data: isize, + pub i128_data: i128, + pub i64_data: i64, + pub i32_data: i32, + pub i16_data: i16, + pub i8_data: i8, + pub bool_data: bool, + pub char_data: char, + pub f32_data: f32, + pub f64_data: f64, + pub str_data: String, +} + +impl BoundaryStruct { + fn new() -> Self { + BoundaryStruct { + usize_data: 0, + u128_data: 0, + u64_data: 0, + u32_data: 0, + u16_data: 0, + u8_data: 0, + isize_data: 0, + i128_data: 0, + i64_data: 0, + i32_data: 0, + i16_data: 0, + i8_data: 0, + bool_data: false, + char_data: 'a', + f32_data: 0.0, + f64_data: 0.0, + str_data: "".to_string(), + } + } + + pub fn min_value() -> Self { + BoundaryStruct { + usize_data: usize::MIN, + u128_data: u128::MIN, + u64_data: u64::MIN, + u32_data: u32::MIN, + u16_data: u16::MIN, + u8_data: u8::MIN, + isize_data: isize::MIN, + i128_data: i128::MIN, + i64_data: i64::MIN, + i32_data: i32::MIN, + i16_data: i16::MIN, + i8_data: i8::MIN, + bool_data: false, + char_data: ' ', + f32_data: f32::MIN, + f64_data: f64::MIN, + str_data: "".to_string(), + } + } + + pub fn max_value() -> Self { + BoundaryStruct { + usize_data: usize::MAX, + u128_data: u128::MAX, + u64_data: u64::MAX, + u32_data: u32::MAX, + u16_data: u16::MAX, + u8_data: u8::MAX, + isize_data: 0, + i128_data: i128::MAX, + i64_data: i64::MAX, + i32_data: i32::MAX, + i16_data: i16::MAX, + i8_data: i8::MAX, + bool_data: true, + char_data: '0', + f32_data: f32::MAX, + f64_data: f64::MAX, + str_data: "max".to_string(), + } + } +} + +#[ckb_script_ipc::service] +pub trait TestBoundary { + fn test(vec: Vec) -> Vec; +} + +#[ckb_script_ipc::service] +pub trait TestLinkedCall { + fn test_linked_call() -> usize; + fn test_linked_call_self(count: usize) -> usize; + + fn get_data() -> usize; +} diff --git a/demo/contracts/ckb-script-ipc-test/src/large_data_client.rs b/demo/contracts/ckb-script-ipc-test/src/large_data_client.rs new file mode 100644 index 0000000..25d7611 --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/large_data_client.rs @@ -0,0 +1,91 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod error; +use ckb_std::debug; + +use alloc::ffi::CString; +use alloc::string::String; +use alloc::vec::Vec; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::high_level::{load_cell_data, QueryIter}; +use ckb_std::{ckb_constants::Source, log::info}; + +use crate::error::Error; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use ckb_std::syscalls::current_cycles; + +pub mod def; +use crate::def::TestLargeDataClient; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + let mut client = TestLargeDataClient::new(read_pipe, write_pipe); + + let data_length = collect_outputs_amount().unwrap(); + + // invoke + let ret = client.test_large_data_handling(generate_rand_str(data_length as usize).into()); + info!("IPC response: {:?}", ret); + + Ok(()) +} +const UDT_LEN: usize = 16; + +fn generate_rand_str(len: usize) -> String { + let mut s = String::new(); + for _ in 0..len { + s.push((current_cycles() as u8 % 26 + 97) as char); + } + s +} + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut buf = [0u8; UDT_LEN]; + debug!( + "QueryIter:{:?}", + QueryIter::new(load_cell_data, Source::GroupOutput).count() + ); + let udt_list = QueryIter::new(load_cell_data, Source::GroupOutput) + .map(|data| { + if data.len() == UDT_LEN { + buf.copy_from_slice(&data); + // u128 is 16 bytes + Ok(u128::from_le_bytes(buf)) + } else { + Err(9) + } + }) + .collect::, u8>>()?; + Ok(udt_list.into_iter().sum::()) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/large_data_client_2.rs b/demo/contracts/ckb-script-ipc-test/src/large_data_client_2.rs new file mode 100644 index 0000000..a22910e --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/large_data_client_2.rs @@ -0,0 +1,87 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; +pub mod def; +use crate::def::TestLargeDataClient; + +pub mod error; +use ckb_std::debug; + +use alloc::ffi::CString; +use alloc::string::String; +use alloc::vec::Vec; +use ckb_script_ipc_common::spawn::spawn_server; +use ckb_std::high_level::{load_cell_data, QueryIter}; +use ckb_std::{ckb_constants::Source, log::info}; + +use crate::error::Error; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; +use ckb_std::syscalls::current_cycles; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + // server can be spawned by any process which wants to start it. + // here it is invoked by client + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = TestLargeDataClient::new(read_pipe, write_pipe); + + let data_length = collect_outputs_amount().unwrap(); + + // invoke + let ret = client.test_large_data_handling2(generate_rand_v8s(data_length as usize).into()); + info!("IPC response: {:?}", ret); + + Ok(()) +} +const UDT_LEN: usize = 16; + +fn generate_rand_v8s(len: usize) -> Vec { + (0..len).map(|_| 1).collect() +} + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut buf = [0u8; UDT_LEN]; + debug!( + "QueryIter:{:?}", + QueryIter::new(load_cell_data, Source::GroupOutput).count() + ); + let udt_list = QueryIter::new(load_cell_data, Source::GroupOutput) + .map(|data| { + if data.len() == UDT_LEN { + buf.copy_from_slice(&data); + // u128 is 16 bytes + Ok(u128::from_le_bytes(buf)) + } else { + Err(9) + } + }) + .collect::, u8>>()?; + Ok(udt_list.into_iter().sum::()) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/large_data_server.rs b/demo/contracts/ckb-script-ipc-test/src/large_data_server.rs new file mode 100644 index 0000000..3e9dcbc --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/large_data_server.rs @@ -0,0 +1,74 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use alloc::vec::Vec; +use ckb_std::log::info; + +use crate::def::TestLargeData; +use alloc::{format, string::String}; +use ckb_script_ipc_common::spawn::run_server; + +use crate::error::Error; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub struct LargeDataServer { + pub data: usize, +} + +impl LargeDataServer { + fn new() -> Self { + LargeDataServer { data: 2 } + } +} + +impl TestLargeData for LargeDataServer { + // method implementation + fn test_large_data_handling(&mut self, data: String) -> String { + self.data += 1; + return format!("hello, {}", data); + } + fn test_large_data_handling2(&mut self, data: Vec) -> Vec { + return data; + } +} + +// impl World for LargeDataServer { +// // method implementation +// fn hello(&mut self, name: String) -> Result { +// self.data += 1; +// if name == "error" { +// Err(1) +// } else { +// Ok(format!("hello, {}", name)) +// } +// } +// fn get_data(&mut self) -> usize { +// return self.data; +// } +// } + +pub fn server_run() -> Result<(), Error> { + let world = LargeDataServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/linked_client.rs b/demo/contracts/ckb-script-ipc-test/src/linked_client.rs new file mode 100644 index 0000000..8214f8f --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/linked_client.rs @@ -0,0 +1,85 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod error; +use ckb_std::debug; + +use crate::error::Error; +use alloc::ffi::CString; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use ckb_script_ipc_common::spawn::spawn_server; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::high_level::{load_cell_data, QueryIter}; +use ckb_std::logger; +use ckb_std::syscalls::current_cycles; +use ckb_std::{ckb_constants::Source, log::info}; + +pub mod def; +use crate::def::TestLinkedCallClient; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match client_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub fn client_run() -> Result<(), Error> { + info!("client run started"); + + let (read_pipe, write_pipe) = spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError)?; + + // new client + let mut client = TestLinkedCallClient::new(read_pipe, write_pipe); + + let data_length = collect_outputs_amount().unwrap(); + + let ret = client.test_linked_call_self(data_length as usize); + // invoke + let expected_ret = (1 + data_length) * data_length / 2; + info!("IPC response: {:?}", ret); + assert_eq!(ret, expected_ret as usize); + + Ok(()) +} +const UDT_LEN: usize = 16; + + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut buf = [0u8; UDT_LEN]; + debug!( + "QueryIter:{:?}", + QueryIter::new(load_cell_data, Source::GroupOutput).count() + ); + let udt_list = QueryIter::new(load_cell_data, Source::GroupOutput) + .map(|data| { + if data.len() == UDT_LEN { + buf.copy_from_slice(&data); + // u128 is 16 bytes + Ok(u128::from_le_bytes(buf)) + } else { + Err(9) + } + }) + .collect::, u8>>()?; + Ok(udt_list.into_iter().sum::()) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/linked_server.rs b/demo/contracts/ckb-script-ipc-test/src/linked_server.rs new file mode 100644 index 0000000..bac9ad5 --- /dev/null +++ b/demo/contracts/ckb-script-ipc-test/src/linked_server.rs @@ -0,0 +1,120 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +pub mod def; +pub mod error; + +use ckb_std::{debug, syscalls}; + +use crate::def::{TestLinkedCall, TestLinkedCallClient}; +use crate::error::Error; +use alloc::ffi::CString; +use ckb_script_ipc_common::pipe::Pipe; +use ckb_script_ipc_common::spawn::{run_server, spawn_server}; +use ckb_std::ckb_constants::Source; +#[cfg(not(test))] +use ckb_std::default_alloc; +use ckb_std::logger; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +pub fn program_entry() -> i8 { + drop(logger::init()); + match server_run() { + Ok(_) => 0, + Err(e) => e as i8, + } +} + +pub struct LinkedCallServer { + pub data: u8, +} + +impl LinkedCallServer { + fn new() -> Self { + debug!("LinkedCallServer new,{}", syscalls::process_id()); + if syscalls::process_id() > 1 { + return LinkedCallServer { + data: syscalls::process_id() as u8, + }; + } + let (read_pipe, write_pipe) = match spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError) + { + Ok(ret) => ret, + Err(_) => { + debug!("{}:spawn server failed", syscalls::process_id()); + panic!("spawn server failed"); + } + }; + + // new client + let mut client = TestLinkedCallClient::new(read_pipe, write_pipe); + let ret = client.get_data(); + return LinkedCallServer { data: ret as u8 }; + } +} + +impl TestLinkedCall for LinkedCallServer { + // method implementation + + fn test_linked_call(&mut self) -> usize { + return 0; + } + fn test_linked_call_self(&mut self, count: usize) -> usize { + debug!("LinkedCallServer new,{}", syscalls::process_id()); + if count == 0 { + return 0; + } + let (read_pipe, write_pipe) = match spawn_server( + 0, + Source::CellDep, + &[CString::new("demo").unwrap().as_ref()], + ) + .map_err(|_| Error::CkbSysError) + { + Ok(ret) => ret, + Err(_) => { + debug!("{}:spawn server failed", syscalls::process_id()); + panic!("spawn server failed"); + } + }; + // new client + let mut client = TestLinkedCallClient::new(read_pipe, write_pipe); + return count + client.test_linked_call_self(count - 1); + } + + fn get_data(&mut self) -> usize { + return syscalls::process_id() as usize; + } +} + +// impl World for LargeDataServer { +// // method implementation +// fn hello(&mut self, name: String) -> Result { +// self.data += 1; +// if name == "error" { +// Err(1) +// } else { +// Ok(format!("hello, {}", name)) +// } +// } +// fn get_data(&mut self) -> usize { +// return self.data; +// } +// } + +pub fn server_run() -> Result<(), Error> { + let world = LinkedCallServer::new(); + run_server(world.server()).map_err(|_| Error::ServerError) +} diff --git a/demo/contracts/ckb-script-ipc-test/src/server.rs b/demo/contracts/ckb-script-ipc-test/src/server.rs index 97f2b1d..cde6f9d 100644 --- a/demo/contracts/ckb-script-ipc-test/src/server.rs +++ b/demo/contracts/ckb-script-ipc-test/src/server.rs @@ -7,16 +7,16 @@ extern crate alloc; pub mod def; pub mod error; -use ckb_std::{log::info}; +use ckb_std::log::info; use crate::def::World; use alloc::{format, string::String}; use ckb_script_ipc_common::spawn::run_server; +use crate::error::Error; #[cfg(not(test))] use ckb_std::default_alloc; use ckb_std::logger; -use crate::error::Error; #[cfg(not(test))] ckb_std::entry!(program_entry); @@ -37,9 +37,7 @@ struct WorldServer { impl WorldServer { fn new() -> Self { - WorldServer { - data: 2 - } + WorldServer { data: 2 } } } @@ -62,4 +60,4 @@ impl World for WorldServer { pub fn server_run() -> Result<(), Error> { let world = WorldServer::new(); run_server(world.server()).map_err(|_| Error::ServerError) -} \ No newline at end of file +} diff --git a/demo/contracts/exec-contract/.gitignore b/demo/contracts/exec-contract/.gitignore new file mode 100644 index 0000000..c3dca1b --- /dev/null +++ b/demo/contracts/exec-contract/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/demo/contracts/exec-contract/Cargo.toml b/demo/contracts/exec-contract/Cargo.toml new file mode 100644 index 0000000..7c06aa9 --- /dev/null +++ b/demo/contracts/exec-contract/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "exec-contract" +version = "0.1.0" +edition = "2021" + +[dependencies] +ckb-std = "0.17.0" + +[features] +native-simulator = ["ckb-std/native-simulator"] + + +[[bin]] +name = "exec-arg-length" +path = "src/exec_arg_length.rs" \ No newline at end of file diff --git a/demo/contracts/exec-contract/Makefile b/demo/contracts/exec-contract/Makefile new file mode 100644 index 0000000..4bfb6ac --- /dev/null +++ b/demo/contracts/exec-contract/Makefile @@ -0,0 +1,80 @@ +# We cannot use $(shell pwd), which will return unix path format on Windows, +# making it hard to use. +cur_dir = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +TOP := $(cur_dir) +# RUSTFLAGS that are likely to be tweaked by developers. For example, +# while we enable debug logs by default here, some might want to strip them +# for minimal code size / consumed cycles. +CUSTOM_RUSTFLAGS := -C debug-assertions +# RUSTFLAGS that are less likely to be tweaked by developers. Most likely +# one would want to keep the default values here. +FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) +# Additional cargo args to append here. For example, one can use +# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to +# stdout in unit tests +CARGO_ARGS := +MODE := release +# Tweak this to change the clang version to use for building C code. By default +# we use a bash script with somes heuristics to find clang in current system. +CLANG := $(shell $(TOP)/scripts/find_clang) +AR := $(subst clang,llvm-ar,$(CLANG)) +OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG)) +# When this is set to some value, the generated binaries will be copied over +BUILD_DIR := +# Generated binaries to copy. By convention, a Rust crate's directory name will +# likely match the crate name, which is also the name of the final binary. +# However if this is not the case, you can tweak this variable. As the name hints, +# more than one binary is supported here. +BINARIES := "exec-arg-length" + +ifeq (release,$(MODE)) + MODE_ARGS := --release +endif + +default: build test + +build: + RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \ + cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS) + @set -eu; \ + if [ "x$(BUILD_DIR)" != "x" ]; then \ + for binary in $(BINARIES); do \ + echo "Copying binary $$binary to build directory"; \ + cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \ + cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \ + $(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \ + done \ + fi + +# test, check, clippy and fmt here are provided for completeness, +# there is nothing wrong invoking cargo directly instead of make. +test: + cargo test $(CARGO_ARGS) + +check: + cargo check $(CARGO_ARGS) + +clippy: + cargo clippy $(CARGO_ARGS) + +fmt: + cargo fmt $(CARGO_ARGS) + +# Arbitrary cargo command is supported here. For example: +# +# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly" +# +# Invokes: +# cargo expand --ugly +CARGO_CMD := +cargo: + cargo $(CARGO_CMD) $(CARGO_ARGS) + +clean: + cargo clean + +prepare: + rustup target add riscv64imac-unknown-none-elf + +.PHONY: build test check clippy fmt cargo clean prepare diff --git a/demo/contracts/exec-contract/README.md b/demo/contracts/exec-contract/README.md new file mode 100644 index 0000000..a4991d4 --- /dev/null +++ b/demo/contracts/exec-contract/README.md @@ -0,0 +1,7 @@ +# first-contract + +TODO: Write this readme + +*This contract was bootstrapped with [ckb-script-templates].* + +[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates diff --git a/demo/contracts/exec-contract/src/exec_arg_length.rs b/demo/contracts/exec-contract/src/exec_arg_length.rs new file mode 100644 index 0000000..28176a9 --- /dev/null +++ b/demo/contracts/exec-contract/src/exec_arg_length.rs @@ -0,0 +1,119 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +#[cfg(test)] +extern crate alloc; + +use ckb_std::{debug, syscalls}; +use ckb_std::high_level::{load_cell_data, QueryIter}; + + +use core::u64; + +use alloc::vec::Vec; +use ckb_std::ckb_constants::{Source, SYS_EXEC}; +#[cfg(not(test))] +use ckb_std::default_alloc; + +#[cfg(not(test))] +ckb_std::entry!(program_entry); +#[cfg(not(test))] +default_alloc!(); + +fn exec_test_limit() { + if syscalls::current_cycles() > 99000 { + return; + } + let mb = [0x66 as i8; 1024 * 1024]; + let kb = [0x66 as i8; 1024]; + let bt = [0x66 as i8; 1]; + + // s[3145727] = 0; + unsafe { + // let size = collect_outputs_amount(); + // let argv = [s.as_ptr() as *const i8; (1024*512)/8]; + // 读指针的时候每次加 8 会越界的 + // + let (mb_size, kb_size, byte_size) = get_data(collect_outputs_amount().unwrap() as usize); + let mut argv: Vec<*const i8> = Vec::with_capacity(mb_size as usize); + for _ in 0..mb_size { + argv.push(mb.as_ptr()); + } + for _ in 0..kb_size { + argv.push(kb.as_ptr()); + } + for _ in 0..byte_size { + argv.push(bt.as_ptr()); + } + let argc = argv.len(); + syscall( + 0, + 3, + 0, + 0, + argc as u64, + argv.as_ptr() as u64, + 0, + SYS_EXEC, + ); + } +} + +pub fn get_data(u128: usize) -> (usize, usize, usize) { + debug!("u128:{}",u128); + debug!("mb:{},kb:{},byte:{}",u128 / 100000000, u128 % 100000000 / 10000,u128 % 10000); + return (u128 / 100000000, u128 % 100000000 / 10000, u128 % 10000); +} +pub fn program_entry() -> i8 { + exec_test_limit(); + 0 +} + +const UDT_LEN: usize = 16; + +fn collect_outputs_amount() -> Result { + // With the sum of all input UDT tokens gathered, let's now iterate through + // output cells to grab the sum of all output UDT tokens. + let mut buf = [0u8; UDT_LEN]; + debug!("QueryIter:{:?}",QueryIter::new(load_cell_data, Source::GroupOutput).count()); + let udt_list = QueryIter::new(load_cell_data, Source::GroupOutput) + .map(|data| { + if data.len() == UDT_LEN { + buf.copy_from_slice(&data); + // u128 is 16 bytes + Ok(u128::from_le_bytes(buf)) + } else { + Err(9) + } + }).collect::, u8>>()?; + Ok(udt_list.into_iter().sum::()) +} + + +#[cfg(target_arch = "riscv64")] +use core::arch::asm; + +// #[cfg(target_arch = "riscv64")] +unsafe fn syscall( + mut a0: u64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, +) -> u64 { + asm!( + "ecall", + inout("a0") a0, + in("a1") a1, + in("a2") a2, + in("a3") a3, + in("a4") a4, + in("a5") a5, + in("a6") a6, + in("a7") a7 + ); + a0 +} \ No newline at end of file diff --git a/src/tests/ckb_script_ipc_boundary.rs b/src/tests/ckb_script_ipc_boundary.rs new file mode 100644 index 0000000..66be4d2 --- /dev/null +++ b/src/tests/ckb_script_ipc_boundary.rs @@ -0,0 +1,23 @@ +use ckb_testtool::ckb_types::core::TransactionBuilder; +use ckb_testtool::ckb_types::prelude::Builder; +use crate::cells::xudt_data::{XUDTData, XUDTDataCell}; +use crate::ContractUtil; +use crate::prelude::ContextExt; + + +#[test] +fn test_boundary_values(){ + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 65439 }); + let mut ct = ContractUtil::new(); + let server_contract = ct.deploy_contract("../../demo/build/release/boundary_server"); + let client_contract = ct.deploy_contract("../../demo/build/release/boundary_client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_passed(&tx, 100000000); + +} + diff --git a/src/tests/ckb_script_ipc_larger.rs b/src/tests/ckb_script_ipc_larger.rs new file mode 100644 index 0000000..98e2456 --- /dev/null +++ b/src/tests/ckb_script_ipc_larger.rs @@ -0,0 +1,59 @@ +use ckb_testtool::ckb_types::core::TransactionBuilder; +use ckb_testtool::ckb_types::prelude::Builder; +use crate::cells::xudt_data::{XUDTData, XUDTDataCell}; +use crate::ContractUtil; +use crate::prelude::ContextExt; + +/// Test large data handling +/// +/// max str length: 65439 +#[test] +fn test_large_data_handling() { + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 65439 }); + let mut ct = ContractUtil::new(); + let server_contract = ct.deploy_contract("../../demo/build/release/large_data_server"); + let client_contract = ct.deploy_contract("../../demo/build/release/large_data_client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_passed(&tx, 100000000); + + let mut tx = TransactionBuilder::default().build(); + input_token_cell.data.amount = 65440; + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_failed(&tx, 100000000); +} + + +#[test] +fn test_large_data_handling_2() { + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 24545 }); + let mut ct = ContractUtil::new(); + let server_contract = ct.deploy_contract("../../demo/build/release/large_data_server"); + let client_contract = ct.deploy_contract("../../demo/build/release/large_data_client2"); + + // for i in 0..100 { + // input_token_cell.data.amount = input_token_cell.data.amount + i * 1; + // println!("input_token_cell.data.amount:{}", input_token_cell.data.amount); + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_passed(&tx, 100000000); + // } + + let mut tx = TransactionBuilder::default().build(); + input_token_cell.data.amount = 24551; + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_failed(&tx, 100000000); +} diff --git a/src/tests/ckb_script_linked_call.rs b/src/tests/ckb_script_linked_call.rs new file mode 100644 index 0000000..3637e90 --- /dev/null +++ b/src/tests/ckb_script_linked_call.rs @@ -0,0 +1,72 @@ +use ckb_testtool::ckb_types::core::TransactionBuilder; +use ckb_testtool::ckb_types::prelude::Builder; +use crate::cells::xudt_data::{XUDTData, XUDTDataCell}; +use crate::ContractUtil; +use crate::prelude::ContextExt; + + +#[test] +fn test_basic_chain_call() { + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 13 }); + let mut ct = ContractUtil::new(); + let server_contract = ct.deploy_contract("../../demo/build/release/linked_server"); + let client_contract = ct.deploy_contract("../../demo/build/release/linked_client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_passed(&tx, 100000000); + + input_token_cell.data.amount = 14; + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_failed(&tx, 100000000); +} + + +#[test] +fn test_complex_chain_call() { + // linked_server.rs + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 1 }); + let mut ct = ContractUtil::new(); + let linked_server_contract = ct.deploy_contract("../../demo/build/release/linked_server"); + let boundary_server_contract = ct.deploy_contract("../../demo/build/release/boundary_server"); + let large_data_server_contract = ct.deploy_contract("../../demo/build/release/large_data_server"); + let server_contract = ct.deploy_contract("../../demo/build/release/ckb-script-ipc-test-serve"); + let complex_chain_call_contract = ct.deploy_contract("../../demo/build/release/complex_chain_call_client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(complex_chain_call_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(complex_chain_call_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &linked_server_contract); + tx = ct.add_contract_cell_dep(tx, &boundary_server_contract); + tx = ct.add_contract_cell_dep(tx, &large_data_server_contract); + tx = ct.add_contract_cell_dep(tx, &server_contract); + + tx = ct.context.complete_tx(tx); + ct.context.should_be_passed(&tx, 1000000000); +} + + +#[test] +fn test_chain_error_propagation() { + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 13 }); + let mut ct = ContractUtil::new(); + let server_contract = ct.deploy_contract("../../demo/build/release/boundary_server"); + let client_contract = ct.deploy_contract("../../demo/build/release/chain_error_propagation_client"); + + let mut tx = TransactionBuilder::default().build(); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); + tx = ct.add_contract_cell_dep(tx, &server_contract); + tx = ct.context.complete_tx(tx); + ct.context.should_be_passed(&tx, 100000000); + + + +} \ No newline at end of file diff --git a/src/tests/exec.rs b/src/tests/exec.rs new file mode 100644 index 0000000..43cc279 --- /dev/null +++ b/src/tests/exec.rs @@ -0,0 +1,18 @@ +use crate::ContractUtil; +use ckb_testtool::ckb_types::core::TransactionBuilder; +use crate::cells::demo::Demo; +use crate::cells::xudt_data::{XUDTData, XUDTDataCell}; +use crate::prelude::ContextExt; + +#[test] +fn test_exec_arg_length() { + let mut ct = ContractUtil::new(); + let type_contract = ct.deploy_contract("../../demo/build/release/exec-arg-length"); + let mut tx = TransactionBuilder::default().build(); + let input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 110240000 }); + tx = ct.add_input(tx, ct.alway_contract.clone(), Some(type_contract.clone()), &input_token_cell, 100); + tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(type_contract.clone()), &input_token_cell, 100); + tx = ct.context.complete_tx(tx); + let ret1 = ct.context.should_be_failed(&tx, 1000000); + println!("ret:{:?}", ret1); +} \ No newline at end of file diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 8e11946..a04da82 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -9,3 +9,7 @@ mod output_type_proxy_lock; mod single_use_lock; mod type_burn_lock; mod ckb_script_ipc_demo; +mod exec; +mod ckb_script_ipc_larger; +mod ckb_script_ipc_boundary; +mod ckb_script_linked_call; From 63c552e676cea6d8bc941cce3928c3fc6236f3ae Mon Sep 17 00:00:00 2001 From: gpBlockchain <744158715@qq.com> Date: Fri, 7 Mar 2025 22:04:52 +0800 Subject: [PATCH 07/18] update tool chain --- rust-toolchain.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..c77ddc8 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.84.0" \ No newline at end of file From 7d2cdaf5a148866fc1608cdbe84cb8504f3231b2 Mon Sep 17 00:00:00 2001 From: gpBlockchain <744158715@qq.com> Date: Fri, 7 Mar 2025 22:08:47 +0800 Subject: [PATCH 08/18] fix build failed --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0a0989..51abd05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: 1.84.1 override: true - name: Cache Rust dependencies From 5e2680a655564c7fdd598b288eaada761924a860 Mon Sep 17 00:00:00 2001 From: gpBlockchain <744158715@qq.com> Date: Fri, 7 Mar 2025 22:16:42 +0800 Subject: [PATCH 09/18] update test --- src/tests/ckb_script_linked_call.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/ckb_script_linked_call.rs b/src/tests/ckb_script_linked_call.rs index 3637e90..ac37813 100644 --- a/src/tests/ckb_script_linked_call.rs +++ b/src/tests/ckb_script_linked_call.rs @@ -7,7 +7,7 @@ use crate::prelude::ContextExt; #[test] fn test_basic_chain_call() { - let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 13 }); + let mut input_token_cell = XUDTDataCell::new([1; 32], XUDTData { amount: 14 }); let mut ct = ContractUtil::new(); let server_contract = ct.deploy_contract("../../demo/build/release/linked_server"); let client_contract = ct.deploy_contract("../../demo/build/release/linked_client"); @@ -19,7 +19,7 @@ fn test_basic_chain_call() { tx = ct.context.complete_tx(tx); ct.context.should_be_passed(&tx, 100000000); - input_token_cell.data.amount = 14; + input_token_cell.data.amount = 15; let mut tx = TransactionBuilder::default().build(); tx = ct.add_input(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); @@ -65,8 +65,8 @@ fn test_chain_error_propagation() { tx = ct.add_outpoint(tx, ct.alway_contract.clone(), Some(client_contract.clone()), &input_token_cell, 100); tx = ct.add_contract_cell_dep(tx, &server_contract); tx = ct.context.complete_tx(tx); - ct.context.should_be_passed(&tx, 100000000); + ct.context.should_be_failed(&tx, 100000000); + - } \ No newline at end of file From dca6ae8f42776ebc9d0254147e6810a9b16a25d0 Mon Sep 17 00:00:00 2001 From: gpBlockchain <744158715@qq.com> Date: Fri, 7 Mar 2025 22:37:26 +0800 Subject: [PATCH 10/18] fix build --- demo/contracts/ckb-script-ipc-test/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/contracts/ckb-script-ipc-test/Cargo.toml b/demo/contracts/ckb-script-ipc-test/Cargo.toml index f3bf5b8..09e2409 100644 --- a/demo/contracts/ckb-script-ipc-test/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-test/Cargo.toml @@ -3,6 +3,7 @@ name = "ckb-script-ipc-test" version = "1.0.0" edition = "2021" +[dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } From 99373d99d516b4d26b990591bef916ec344f3c86 Mon Sep 17 00:00:00 2001 From: gpBlockchain <744158715@qq.com> Date: Fri, 7 Mar 2025 23:09:13 +0800 Subject: [PATCH 11/18] remove enable log --- demo/contracts/ckb-script-ipc-test/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/contracts/ckb-script-ipc-test/Cargo.toml b/demo/contracts/ckb-script-ipc-test/Cargo.toml index 09e2409..ab0ad8c 100644 --- a/demo/contracts/ckb-script-ipc-test/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-test/Cargo.toml @@ -5,7 +5,8 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +#ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.1" } serde = { version = "1.0", default-features = false, features = ["derive"] } ckb-script-ipc = { version = "1.0.1" } log = "0.4.25" From 884ed77b847b701c67648e7e6e6d4a42894a2885 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Tue, 25 Mar 2025 14:36:11 +0800 Subject: [PATCH 12/18] add fuzz for serialize case --- demo/Makefile | 2 +- demo/contracts/ckb-script-ipc-demo/Makefile | 2 +- demo/contracts/ckb-script-ipc-test/Makefile | 2 +- demo/contracts/test_invalid_request/Makefile | 2 +- .../test_large_data_handling/Makefile | 2 +- demo/contracts/test_loop_request/Makefile | 2 +- demo/contracts/test_serialize/Makefile | 2 +- demo/contracts/test_serialize/src/client.rs | 193 +++++++++++++++++- demo/contracts/test_serialize/src/server.rs | 2 - .../contracts/test_serialize_complex/Makefile | 2 +- .../test_single_loop_request/Makefile | 2 +- 11 files changed, 201 insertions(+), 12 deletions(-) diff --git a/demo/Makefile b/demo/Makefile index 6cd1398..58b88a7 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := -C debug-assertions +CUSTOM_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a -C debug-assertions=yes # Additional cargo args to append here. For example, one can use # make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to # stdout in unit tests diff --git a/demo/contracts/ckb-script-ipc-demo/Makefile b/demo/contracts/ckb-script-ipc-demo/Makefile index 9cf627c..53b59eb 100644 --- a/demo/contracts/ckb-script-ipc-demo/Makefile +++ b/demo/contracts/ckb-script-ipc-demo/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/ckb-script-ipc-test/Makefile b/demo/contracts/ckb-script-ipc-test/Makefile index 5681ab0..38531e2 100644 --- a/demo/contracts/ckb-script-ipc-test/Makefile +++ b/demo/contracts/ckb-script-ipc-test/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/test_invalid_request/Makefile b/demo/contracts/test_invalid_request/Makefile index 58027f7..2689b9b 100644 --- a/demo/contracts/test_invalid_request/Makefile +++ b/demo/contracts/test_invalid_request/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/test_large_data_handling/Makefile b/demo/contracts/test_large_data_handling/Makefile index 1b811d3..13ca8ac 100644 --- a/demo/contracts/test_large_data_handling/Makefile +++ b/demo/contracts/test_large_data_handling/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/test_loop_request/Makefile b/demo/contracts/test_loop_request/Makefile index f67ebc7..dcbd606 100644 --- a/demo/contracts/test_loop_request/Makefile +++ b/demo/contracts/test_loop_request/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/test_serialize/Makefile b/demo/contracts/test_serialize/Makefile index ae3a02e..23c8105 100644 --- a/demo/contracts/test_serialize/Makefile +++ b/demo/contracts/test_serialize/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/test_serialize/src/client.rs b/demo/contracts/test_serialize/src/client.rs index d89fe31..be10460 100644 --- a/demo/contracts/test_serialize/src/client.rs +++ b/demo/contracts/test_serialize/src/client.rs @@ -9,7 +9,7 @@ pub mod error; use alloc::ffi::CString; use ckb_script_ipc_common::spawn::spawn_server; -use ckb_std::{ckb_constants::Source, log::info}; +use ckb_std::{ckb_constants::Source, log::info, syscalls}; #[cfg(not(test))] use ckb_std::default_alloc; @@ -31,6 +31,115 @@ pub fn program_entry() -> i8 { } } +// 简单的伪随机数生成器 +struct FuzzGenerator { + seed: u64, +} + +impl FuzzGenerator { + fn new() -> Self { + // 使用当前周期作为随机种子 + let seed = syscalls::current_cycles() as u64; + Self { seed } + } + + // 生成下一个随机数 + fn next(&mut self) -> u64 { + // 简单的线性同余生成器 + self.seed = self.seed.wrapping_mul(6364136223846793005).wrapping_add(1); + self.seed + } + + // 生成指定范围内的随机数 + fn range(&mut self, min: u64, max: u64) -> u64 { + min + (self.next() % (max - min + 1)) + } + + // 生成随机i8 + fn gen_i8(&mut self) -> i8 { + self.next() as i8 + } + + // 生成随机i16 + fn gen_i16(&mut self) -> i16 { + self.next() as i16 + } + + // 生成随机i32 + fn gen_i32(&mut self) -> i32 { + self.next() as i32 + } + + // 生成随机i64 + fn gen_i64(&mut self) -> i64 { + self.next() as i64 + } + + // 生成随机i128 + fn gen_i128(&mut self) -> i128 { + (self.next() as i128) | ((self.next() as i128) << 64) + } + + // 生成随机u8 + fn gen_u8(&mut self) -> u8 { + self.next() as u8 + } + + // 生成随机u16 + fn gen_u16(&mut self) -> u16 { + self.next() as u16 + } + + // 生成随机u32 + fn gen_u32(&mut self) -> u32 { + self.next() as u32 + } + + // 生成随机u64 + fn gen_u64(&mut self) -> u64 { + self.next() + } + + // 生成随机u128 + fn gen_u128(&mut self) -> u128 { + (self.next() as u128) | ((self.next() as u128) << 64) + } + + // 生成随机f32 + fn gen_f32(&mut self) -> f32 { + let bits = self.next() as u32; + f32::from_bits(bits) + } + + // 生成随机f64 + fn gen_f64(&mut self) -> f64 { + let bits = self.next(); + f64::from_bits(bits) + } + + // 生成随机bool + fn gen_bool(&mut self) -> bool { + (self.next() & 1) == 1 + } + + // 生成随机char + fn gen_char(&mut self) -> char { + // 生成有效的Unicode字符(基本多文种平面) + let code = self.range(32, 0xD7FF) as u32; + char::from_u32(code).unwrap_or('?') + } + + // 生成随机字符串 + fn gen_string(&mut self, max_len: usize) -> String { + let len = self.range(1, max_len as u64) as usize; + let mut result = String::with_capacity(len); + for _ in 0..len { + result.push(self.gen_char()); + } + result + } +} + // 生成大数据字符串 fn generate_large_data(size_mb: usize) -> String { let base_str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -69,6 +178,20 @@ pub fn client_run() -> Result<(), Error> { // new client let mut client = WorldClient::new(read_pipe, write_pipe); + // 创建Fuzz生成器 + let mut fuzz_gen = FuzzGenerator::new(); + + // 执行标准测试 + run_standard_tests(&mut client)?; + + // 执行Fuzz测试 + run_fuzz_tests(&mut client, &mut fuzz_gen, 10)?; // 执行10轮Fuzz测试 + + Ok(()) +} + +// 标准测试函数 +fn run_standard_tests(client: &mut WorldClient) -> Result<(), Error> { // 测试整数类型 info!("测试整数类型"); info!("i8: {:?}", client.test_i8(-8)); @@ -102,6 +225,74 @@ pub fn client_run() -> Result<(), Error> { // info!("测试字符串类型,数据大小: {} bytes", large_data.len()); // let ret = client.hello(large_data); // info!("字符串响应: {:?}", ret); + + Ok(()) +} +// Fuzz测试函数 +fn run_fuzz_tests(client: &mut WorldClient, fuzz_gen: &mut FuzzGenerator, rounds: usize) -> Result<(), Error> { + info!("开始Fuzz测试,执行{}轮", rounds); + + for i in 0..rounds { + info!("Fuzz测试轮次 {}/{}", i+1, rounds); + + // 测试整数类型 + let i8_val = fuzz_gen.gen_i8(); + info!("Fuzz i8({}): {:?}", i8_val, client.test_i8(i8_val)); + + let i16_val = fuzz_gen.gen_i16(); + info!("Fuzz i16({}): {:?}", i16_val, client.test_i16(i16_val)); + + let i32_val = fuzz_gen.gen_i32(); + info!("Fuzz i32({}): {:?}", i32_val, client.test_i32(i32_val)); + + let i64_val = fuzz_gen.gen_i64(); + info!("Fuzz i64({}): {:?}", i64_val, client.test_i64(i64_val)); + + let i128_val = fuzz_gen.gen_i128(); + info!("Fuzz i128({}): {:?}", i128_val, client.test_i128(i128_val)); + + let u8_val = fuzz_gen.gen_u8(); + info!("Fuzz u8({}): {:?}", u8_val, client.test_u8(u8_val)); + + let u16_val = fuzz_gen.gen_u16(); + info!("Fuzz u16({}): {:?}", u16_val, client.test_u16(u16_val)); + + let u32_val = fuzz_gen.gen_u32(); + info!("Fuzz u32({}): {:?}", u32_val, client.test_u32(u32_val)); + + let u64_val = fuzz_gen.gen_u64(); + info!("Fuzz u64({}): {:?}", u64_val, client.test_u64(u64_val)); + + let u128_val = fuzz_gen.gen_u128(); + info!("Fuzz u128({}): {:?}", u128_val, client.test_u128(u128_val)); + + // 测试浮点类型 + let f32_val = fuzz_gen.gen_f32(); + // 过滤掉NaN和无穷大的情况 + if !f32_val.is_nan() && f32_val.is_finite() { + info!("Fuzz f32({}): {:?}", f32_val, client.test_f32(f32_val)); + } + + let f64_val = fuzz_gen.gen_f64(); + // 过滤掉NaN和无穷大的情况 + if !f64_val.is_nan() && f64_val.is_finite() { + info!("Fuzz f64({}): {:?}", f64_val, client.test_f64(f64_val)); + } + + // 测试布尔类型 + let bool_val = fuzz_gen.gen_bool(); + info!("Fuzz bool({}): {:?}", bool_val, client.test_bool(bool_val)); + + // 测试字符类型 + let char_val = fuzz_gen.gen_char(); + info!("Fuzz char({}): {:?}", char_val, client.test_char(char_val)); + + // 测试字符串类型 + let string_val = fuzz_gen.gen_string(100); // 最大100个字符 + info!("Fuzz string({}): {:?}", string_val.clone(), client.hello(string_val)); + } + + info!("Fuzz测试完成"); Ok(()) } diff --git a/demo/contracts/test_serialize/src/server.rs b/demo/contracts/test_serialize/src/server.rs index b8fdea4..d4c9777 100644 --- a/demo/contracts/test_serialize/src/server.rs +++ b/demo/contracts/test_serialize/src/server.rs @@ -7,8 +7,6 @@ extern crate alloc; pub mod def; pub mod error; -use ckb_std::{log::info}; - use crate::def::World; use alloc::{format, string::String}; use ckb_script_ipc_common::spawn::run_server; diff --git a/demo/contracts/test_serialize_complex/Makefile b/demo/contracts/test_serialize_complex/Makefile index bdcc085..7df39ff 100644 --- a/demo/contracts/test_serialize_complex/Makefile +++ b/demo/contracts/test_serialize_complex/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) diff --git a/demo/contracts/test_single_loop_request/Makefile b/demo/contracts/test_single_loop_request/Makefile index d183b64..94d9c49 100644 --- a/demo/contracts/test_single_loop_request/Makefile +++ b/demo/contracts/test_single_loop_request/Makefile @@ -6,7 +6,7 @@ TOP := $(cur_dir) # RUSTFLAGS that are likely to be tweaked by developers. For example, # while we enable debug logs by default here, some might want to strip them # for minimal code size / consumed cycles. -CUSTOM_RUSTFLAGS := --cfg debug_assertions +CUSTOM_RUSTFLAGS := -C debug-assertions=yes # RUSTFLAGS that are less likely to be tweaked by developers. Most likely # one would want to keep the default values here. FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS) From f70967f726692d3e71a259f707bb3d04bdcc3e61 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Tue, 25 Mar 2025 14:44:49 +0800 Subject: [PATCH 13/18] solve edition2024 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c77ddc8..552d6d6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.84.0" \ No newline at end of file +channel = "1.85.0" \ No newline at end of file From eff8867523faf44ab5fdfed166b2cd671a63a494 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Tue, 25 Mar 2025 14:47:06 +0800 Subject: [PATCH 14/18] ci update toolchain --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51abd05..c4f5e28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.84.1 + toolchain: 1.85.0 override: true - name: Cache Rust dependencies From 282c3047c6e7ec835c358a6cb46cdbe48ed62410 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Tue, 25 Mar 2025 14:57:36 +0800 Subject: [PATCH 15/18] udpate toolchain --- .github/workflows/ci.yml | 18 +++++++++--------- rust-toolchain.toml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4f5e28..9bb67cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,17 +20,17 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.85.0 + toolchain: 1.81.0 override: true - - name: Cache Rust dependencies - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # - name: Cache Rust dependencies + # uses: actions/cache@v3 + # with: + # path: | + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Add RISC-V target run: rustup target add riscv64imac-unknown-none-elf diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 552d6d6..3cf2110 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.85.0" \ No newline at end of file +channel = "1.81.0" \ No newline at end of file From 358d1964037c384729f12769e688f25625eca3b4 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Tue, 25 Mar 2025 15:06:36 +0800 Subject: [PATCH 16/18] solve --- .github/workflows/ci.yml | 18 +++++++++--------- rust-toolchain.toml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bb67cd..d0a0989 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,17 +20,17 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.81.0 + toolchain: stable override: true - # - name: Cache Rust dependencies - # uses: actions/cache@v3 - # with: - # path: | - # ~/.cargo/registry - # ~/.cargo/git - # target - # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Cache Rust dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Add RISC-V target run: rustup target add riscv64imac-unknown-none-elf diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3cf2110..c77ddc8 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.81.0" \ No newline at end of file +channel = "1.84.0" \ No newline at end of file From cc0aedd4447fff2e086eefc6734facbb892a82f8 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Tue, 25 Mar 2025 15:32:08 +0800 Subject: [PATCH 17/18] update toolchain -> 1.85.0 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0a0989..c4f5e28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: 1.85.0 override: true - name: Cache Rust dependencies From 04e59e9a31b15c62ba9b9ffaf84f0693153270d9 Mon Sep 17 00:00:00 2001 From: 15168316096 <15168316096@163.com> Date: Mon, 12 May 2025 15:02:48 +0800 Subject: [PATCH 18/18] update ipc and ipc-common v1.0.3 --- demo/contracts/ckb-script-ipc-demo/Cargo.toml | 4 ++-- demo/contracts/ckb-script-ipc-test/Cargo.toml | 4 ++-- demo/contracts/test_invalid_request/Cargo.toml | 4 ++-- demo/contracts/test_large_data_handling/Cargo.toml | 4 ++-- demo/contracts/test_loop_request/Cargo.toml | 4 ++-- demo/contracts/test_serialize/Cargo.toml | 4 ++-- demo/contracts/test_serialize_complex/Cargo.toml | 4 ++-- demo/contracts/test_single_loop_request/Cargo.toml | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/demo/contracts/ckb-script-ipc-demo/Cargo.toml b/demo/contracts/ckb-script-ipc-demo/Cargo.toml index aff03b2..043d7a2 100644 --- a/demo/contracts/ckb-script-ipc-demo/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-demo/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/ckb-script-ipc-test/Cargo.toml b/demo/contracts/ckb-script-ipc-test/Cargo.toml index ab0ad8c..e5db362 100644 --- a/demo/contracts/ckb-script-ipc-test/Cargo.toml +++ b/demo/contracts/ckb-script-ipc-test/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} #ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } -ckb-script-ipc-common = { version = "1.0.1" } +ckb-script-ipc-common = { version = "1.0.3" } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_invalid_request/Cargo.toml b/demo/contracts/test_invalid_request/Cargo.toml index 2c179a4..bd81b29 100644 --- a/demo/contracts/test_invalid_request/Cargo.toml +++ b/demo/contracts/test_invalid_request/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_large_data_handling/Cargo.toml b/demo/contracts/test_large_data_handling/Cargo.toml index 6102a09..e6f8f27 100644 --- a/demo/contracts/test_large_data_handling/Cargo.toml +++ b/demo/contracts/test_large_data_handling/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_loop_request/Cargo.toml b/demo/contracts/test_loop_request/Cargo.toml index ff4b95a..8eaa4ad 100644 --- a/demo/contracts/test_loop_request/Cargo.toml +++ b/demo/contracts/test_loop_request/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_serialize/Cargo.toml b/demo/contracts/test_serialize/Cargo.toml index c426f50..e305e74 100644 --- a/demo/contracts/test_serialize/Cargo.toml +++ b/demo/contracts/test_serialize/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_serialize_complex/Cargo.toml b/demo/contracts/test_serialize_complex/Cargo.toml index 3beb47d..9e4f173 100644 --- a/demo/contracts/test_serialize_complex/Cargo.toml +++ b/demo/contracts/test_serialize_complex/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive", "rc", "alloc"] } serde_with = { version = "3.6.1", default-features = false, features = ["alloc", "macros"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]] diff --git a/demo/contracts/test_single_loop_request/Cargo.toml b/demo/contracts/test_single_loop_request/Cargo.toml index 8d6f8e1..5b790df 100644 --- a/demo/contracts/test_single_loop_request/Cargo.toml +++ b/demo/contracts/test_single_loop_request/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] ckb-std = { version = "0.17.0", default-features = false, features = ["allocator", "ckb-types", "dummy-atomic", "log"]} -ckb-script-ipc-common = { version = "1.0.1", features = ["enable-logging"] } +ckb-script-ipc-common = { version = "1.0.3", features = ["enable-logging"] } serde = { version = "1.0", default-features = false, features = ["derive"] } -ckb-script-ipc = { version = "1.0.1" } +ckb-script-ipc = { version = "1.0.3" } log = "0.4.25" [[bin]]