From 8d625dbc0f9db9670a60c0d82b12bd7417ae9f6e Mon Sep 17 00:00:00 2001 From: mohanson Date: Fri, 20 Sep 2024 10:31:34 +0800 Subject: [PATCH 01/12] Preserve most of the directory structure --- contracts/ckb-std-tests/Cargo.toml | 4 ++++ contracts/exec-callee/Cargo.toml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/contracts/ckb-std-tests/Cargo.toml b/contracts/ckb-std-tests/Cargo.toml index 94211c0..74d47b5 100644 --- a/contracts/ckb-std-tests/Cargo.toml +++ b/contracts/ckb-std-tests/Cargo.toml @@ -6,7 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +<<<<<<< HEAD ckb-std = { path = "../../", features = ["build-with-clang", "dlopen-c", "log"] } +======= +ckb-std = { path = "../../", features = ["build-with-clang", "dlopen-c", "dummy-atomic", "log"] } +>>>>>>> b5ef88b (Preserve most of the directory structure) blake2b-ref = { version = "0.3", default-features = false } bytes = { version = "1.7", default-features = false } log = { version = "0.4", default-features = false } diff --git a/contracts/exec-callee/Cargo.toml b/contracts/exec-callee/Cargo.toml index a6fbaef..19b9d6c 100644 --- a/contracts/exec-callee/Cargo.toml +++ b/contracts/exec-callee/Cargo.toml @@ -9,4 +9,8 @@ edition = "2018" native-simulator = ["ckb-std/native-simulator"] [dependencies] +<<<<<<< HEAD ckb-std = { path = "../../", default-features=false, features=["allocator", "build-with-clang", "calc-hash", "ckb-types", "libc"] } +======= +ckb-std = { path = "../../", features=["build-with-clang"] } +>>>>>>> b5ef88b (Preserve most of the directory structure) From 6a7aa902e08293976c84392db8dbec2bafabf093 Mon Sep 17 00:00:00 2001 From: mohanson Date: Fri, 20 Sep 2024 13:48:46 +0800 Subject: [PATCH 02/12] Make dummy-atomic as a default feature --- contracts/ckb-std-tests/Cargo.toml | 4 ---- contracts/exec-callee/Cargo.toml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/contracts/ckb-std-tests/Cargo.toml b/contracts/ckb-std-tests/Cargo.toml index 74d47b5..94211c0 100644 --- a/contracts/ckb-std-tests/Cargo.toml +++ b/contracts/ckb-std-tests/Cargo.toml @@ -6,11 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -<<<<<<< HEAD ckb-std = { path = "../../", features = ["build-with-clang", "dlopen-c", "log"] } -======= -ckb-std = { path = "../../", features = ["build-with-clang", "dlopen-c", "dummy-atomic", "log"] } ->>>>>>> b5ef88b (Preserve most of the directory structure) blake2b-ref = { version = "0.3", default-features = false } bytes = { version = "1.7", default-features = false } log = { version = "0.4", default-features = false } diff --git a/contracts/exec-callee/Cargo.toml b/contracts/exec-callee/Cargo.toml index 19b9d6c..a6fbaef 100644 --- a/contracts/exec-callee/Cargo.toml +++ b/contracts/exec-callee/Cargo.toml @@ -9,8 +9,4 @@ edition = "2018" native-simulator = ["ckb-std/native-simulator"] [dependencies] -<<<<<<< HEAD ckb-std = { path = "../../", default-features=false, features=["allocator", "build-with-clang", "calc-hash", "ckb-types", "libc"] } -======= -ckb-std = { path = "../../", features=["build-with-clang"] } ->>>>>>> b5ef88b (Preserve most of the directory structure) From b6b790273ceb9010a868c7854915cf1ba215e56c Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 11:06:39 +0800 Subject: [PATCH 03/12] feat: add Type ID implementation see https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0022-transaction-structure/0022-transaction-structure.md#type-id --- Cargo.toml | 5 + Makefile | 7 +- README.md | 3 +- contracts/ckb-std-tests/src/error.rs | 1 + contracts/exec-callee/src/error.rs | 1 + .../exec-caller-by-code-hash/src/error.rs | 1 + contracts/exec-caller/src/error.rs | 1 + contracts/spawn-callee/src/error.rs | 1 + .../spawn-caller-by-code-hash/src/error.rs | 1 + contracts/spawn-caller/src/error.rs | 1 + examples/type_id.rs | 15 ++ src/error.rs | 3 +- src/lib.rs | 2 + src/type_id.rs | 138 +++++++++++++ test/Cargo.toml | 1 + test/Makefile | 6 +- test/simulator/src/exec_callee.rs | 1 + .../simulator/src/exec_caller_by_code_hash.rs | 1 + test/simulator/src/main.rs | 1 + test/src/exec.rs | 10 +- test/src/lib.rs | 2 + test/src/type_id.rs | 183 ++++++++++++++++++ 22 files changed, 376 insertions(+), 9 deletions(-) create mode 100644 examples/type_id.rs create mode 100644 src/type_id.rs create mode 100644 test/src/type_id.rs diff --git a/Cargo.toml b/Cargo.toml index 2a94f70..66c90e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,12 +26,17 @@ libc = [] # work with `target-feature=-a` Cargo flag dummy-atomic = [] log = ["dep:log", "dummy-atomic"] +# require `ckb-hash` +type-id = ["ckb-hash", "ckb-types"] + [build-dependencies] cc = "1.0" [dependencies] ckb-types = { package = "ckb-gen-types", version = "0.118", default-features = false, optional = true } +ckb-hash = { version = "0.118", default-features = false, features = ["ckb-contract"], optional = true } + buddy-alloc = { version = "0.5", optional = true } ckb-x64-simulator = { version = "0.9", optional = true } gcd = "2.3" diff --git a/Makefile b/Makefile index 8e67093..faa4a70 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,10 @@ CC := riscv64-unknown-elf-gcc default: integration publish-crate: - cargo publish -p ckb-std + cargo publish --target ${TARGET} -p ckb-std + +publish-crate-dryrun: + cargo publish --dry-run --target ${TARGET} -p ckb-std --allow-dirty publish: publish-crate @@ -16,7 +19,7 @@ test-shared-lib: integration: check -test: +test: publish-crate-dryrun make -C test test check: diff --git a/README.md b/README.md index 4aa4632..23efa04 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ckb-std -[![Crates.io](https://img.shields.io/crates/v/ckb-std.svg)](https://crates.io/crates/ckb-std) +[![Crates.io](https://img.shields.io/crates/v/ckb-std.svg)](https://crates.io/crates/ckb-std) This library contains several modules that help you write CKB contract with Rust. @@ -17,6 +17,7 @@ This library contains several modules that help you write CKB contract with Rust * `default_alloc!` macro: defines global allocator for no-std rust * `dummy_atomic` module: dummy atomic operations * `logger` module: colored logger implementation +* `type_id` module: Type ID implementation (feature `type-id`) ### Memory allocator Default allocator uses a mixed allocation strategy: diff --git a/contracts/ckb-std-tests/src/error.rs b/contracts/ckb-std-tests/src/error.rs index c84bbe6..1285caf 100644 --- a/contracts/ckb-std-tests/src/error.rs +++ b/contracts/ckb-std-tests/src/error.rs @@ -29,6 +29,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/contracts/exec-callee/src/error.rs b/contracts/exec-callee/src/error.rs index c84bbe6..1285caf 100644 --- a/contracts/exec-callee/src/error.rs +++ b/contracts/exec-callee/src/error.rs @@ -29,6 +29,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/contracts/exec-caller-by-code-hash/src/error.rs b/contracts/exec-caller-by-code-hash/src/error.rs index c84bbe6..1285caf 100644 --- a/contracts/exec-caller-by-code-hash/src/error.rs +++ b/contracts/exec-caller-by-code-hash/src/error.rs @@ -29,6 +29,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/contracts/exec-caller/src/error.rs b/contracts/exec-caller/src/error.rs index c84bbe6..1285caf 100644 --- a/contracts/exec-caller/src/error.rs +++ b/contracts/exec-caller/src/error.rs @@ -29,6 +29,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/contracts/spawn-callee/src/error.rs b/contracts/spawn-callee/src/error.rs index b78e033..1ee871f 100644 --- a/contracts/spawn-callee/src/error.rs +++ b/contracts/spawn-callee/src/error.rs @@ -28,6 +28,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/contracts/spawn-caller-by-code-hash/src/error.rs b/contracts/spawn-caller-by-code-hash/src/error.rs index b78e033..1ee871f 100644 --- a/contracts/spawn-caller-by-code-hash/src/error.rs +++ b/contracts/spawn-caller-by-code-hash/src/error.rs @@ -28,6 +28,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/contracts/spawn-caller/src/error.rs b/contracts/spawn-caller/src/error.rs index b78e033..1ee871f 100644 --- a/contracts/spawn-caller/src/error.rs +++ b/contracts/spawn-caller/src/error.rs @@ -28,6 +28,7 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/examples/type_id.rs b/examples/type_id.rs new file mode 100644 index 0000000..57dae08 --- /dev/null +++ b/examples/type_id.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +use ckb_std::type_id::check_type_id; +use ckb_std::{default_alloc, entry}; + +entry!(main); +default_alloc!(); + +fn main() -> i8 { + match check_type_id(0) { + Ok(_) => 0, + Err(_) => -10, + } +} diff --git a/src/error.rs b/src/error.rs index 65ccd6c..10634a1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,7 +20,8 @@ pub enum SysError { MaxVmsSpawned, /// Max fds has been spawned. Its value is 9. MaxFdsCreated, - + /// Type ID Error + TypeIDError, /// Unknown syscall error number Unknown(u64), } diff --git a/src/lib.rs b/src/lib.rs index 20eb594..bf87577 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,3 +41,5 @@ pub mod dummy_atomic; pub mod logger; #[cfg(feature = "log")] pub use log; +#[cfg(feature = "type-id")] +pub mod type_id; diff --git a/src/type_id.rs b/src/type_id.rs new file mode 100644 index 0000000..7fd8192 --- /dev/null +++ b/src/type_id.rs @@ -0,0 +1,138 @@ +//! Implementation of Type ID +//! +//! This module provides functionality for validating and checking Type IDs in +//! CKB transactions. It requires "type-id" feature in ckb-std enabled. +//! +//! For more details, see the [Type ID +//! RFC](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0022-transaction-structure/0022-transaction-structure.md#type-id). +//! +//! Note: Type ID cells are allowed to be burned. +//! +use crate::{ + ckb_constants::Source, + error::SysError, + high_level::{load_cell_type_hash, load_input, load_script, load_script_hash, QueryIter}, + syscalls::load_cell, +}; +use ckb_hash::new_blake2b; +use ckb_types::prelude::Entity; + +fn is_cell_present(index: usize, source: Source) -> bool { + let buf = &mut []; + matches!( + load_cell(buf, 0, index, source), + Ok(_) | Err(SysError::LengthNotEnough(_)) + ) +} + +fn locate_index() -> Result { + let hash = load_script_hash()?; + + let index = QueryIter::new(load_cell_type_hash, Source::Output) + .position(|type_hash| type_hash == Some(hash)) + .ok_or(SysError::TypeIDError)?; + + Ok(index) +} + +/// +/// Validates the Type ID in a flexible manner. +/// +/// This function performs a low-level validation of the Type ID. It checks for the +/// presence of cells in the transaction and validates the Type ID based on whether +/// it's a minting operation or a transfer. +/// +/// # Arguments +/// +/// * `type_id` - A 32-byte array representing the Type ID to validate. +/// +/// # Returns +/// +/// * `Ok(())` if the Type ID is valid. +/// * `Err(SysError::TypeIDError)` if the validation fails. +/// +/// # Note +/// +/// For most use cases, it's recommended to use the `check_type_id` function instead, +/// which expects the Type ID to be included in the script `args`. +/// +/// # Examples +/// +/// ```no_run +/// use ckb_std::type_id::validate_type_id; +/// +/// let type_id = [0u8; 32]; +/// validate_type_id(type_id)?; +/// ``` +pub fn validate_type_id(type_id: [u8; 32]) -> Result<(), SysError> { + if is_cell_present(1, Source::GroupInput) || is_cell_present(1, Source::GroupOutput) { + return Err(SysError::TypeIDError); + } + + // mint + if !is_cell_present(0, Source::GroupInput) { + let index = locate_index()?; + let input = load_input(0, Source::Input)?; + let mut blake2b = new_blake2b(); + blake2b.update(input.as_slice()); + blake2b.update(&index.to_le_bytes()); + let mut ret = [0; 32]; + blake2b.finalize(&mut ret); + + if ret != type_id { + return Err(SysError::TypeIDError); + } + } + // For the `else` part, Destroy an old cell with a specific type id and + // create a new cell with the same type id in the same transaction. + Ok(()) +} + +fn load_id_from_args(offset: usize) -> Result<[u8; 32], SysError> { + let script = load_script()?; + let args = script.as_reader().args(); + let args_data = args.raw_data(); + + args_data + .get(offset..offset + 32) + .ok_or(SysError::TypeIDError)? + .try_into() + .map_err(|_| SysError::TypeIDError) +} + +/// +/// Validates that the script follows the Type ID rule. +/// +/// This function checks if the Type ID (a 32-byte value) stored in the script's `args` +/// at the specified offset is valid according to the Type ID rules. +/// +/// # Arguments +/// +/// * `offset` - The byte offset in the script's `args` where the Type ID starts. +/// +/// # Returns +/// +/// * `Ok(())` if the Type ID is valid. +/// * `Err(SysError::TypeIDError)` if the Type ID is invalid or cannot be retrieved. +/// +/// # Examples +/// +/// ```no_run +/// use ckb_std::type_id::check_type_id; +/// +/// fn main() -> Result<(), ckb_std::error::SysError> { +/// // Check the Type ID stored at the beginning of the script args +/// check_type_id(0)?; +/// Ok(()) +/// } +/// ``` +/// +/// # Note +/// +/// This function internally calls `load_id_from_args` to retrieve the Type ID +/// and then `validate_type_id` to perform the actual validation. +pub fn check_type_id(offset: usize) -> Result<(), SysError> { + let type_id = load_id_from_args(offset)?; + validate_type_id(type_id)?; + Ok(()) +} diff --git a/test/Cargo.toml b/test/Cargo.toml index f3b1a66..0aa6378 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -13,3 +13,4 @@ serde_json = "1.0" ckb-mock-tx-types = "0.4.0" blake2b-rs = "0.1.5" faster-hex = "0.6" +ckb-hash = "0.104.0" diff --git a/test/Makefile b/test/Makefile index 6d98db8..9e56ffa 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,4 +1,4 @@ -test: build +test: build build-examples RUST_LOG=debug cargo test -- --nocapture make -C simulator build make -C simulator run @@ -8,6 +8,10 @@ build: make -C shared-lib all-via-docker cd ../contracts && RUSTFLAGS="-C target-feature=-a" cargo build --target riscv64imac-unknown-none-elf +build-examples: + cd ../examples && RUSTFLAGS="-C target-feature=-a" cargo build --example type_id --target riscv64imac-unknown-none-elf --features "type-id" + cd ../examples && RUSTFLAGS="-C target-feature=-a" cargo build --example main --target riscv64imac-unknown-none-elf + clean: rm -rf ../build cargo clean diff --git a/test/simulator/src/exec_callee.rs b/test/simulator/src/exec_callee.rs index ff011ca..273f472 100644 --- a/test/simulator/src/exec_callee.rs +++ b/test/simulator/src/exec_callee.rs @@ -34,6 +34,7 @@ pub mod error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/test/simulator/src/exec_caller_by_code_hash.rs b/test/simulator/src/exec_caller_by_code_hash.rs index 28b899e..dea540d 100644 --- a/test/simulator/src/exec_caller_by_code_hash.rs +++ b/test/simulator/src/exec_caller_by_code_hash.rs @@ -34,6 +34,7 @@ pub mod error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/test/simulator/src/main.rs b/test/simulator/src/main.rs index b91d7a9..6e7f80c 100644 --- a/test/simulator/src/main.rs +++ b/test/simulator/src/main.rs @@ -37,6 +37,7 @@ pub mod error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), + _ => panic!("other sys error"), } } } diff --git a/test/src/exec.rs b/test/src/exec.rs index 3844d19..89ad2f1 100644 --- a/test/src/exec.rs +++ b/test/src/exec.rs @@ -84,10 +84,12 @@ fn test_exec_by_code_hash() { let mut context = Context::default(); let caller_bin = { let mut buf = Vec::new(); - File::open("../contracts/target/riscv64imac-unknown-none-elf/debug/exec-caller-by-code-hash") - .unwrap() - .read_to_end(&mut buf) - .expect("read code"); + File::open( + "../contracts/target/riscv64imac-unknown-none-elf/debug/exec-caller-by-code-hash", + ) + .unwrap() + .read_to_end(&mut buf) + .expect("read code"); Bytes::from(buf) }; let caller_out_point = context.deploy_cell(caller_bin); diff --git a/test/src/lib.rs b/test/src/lib.rs index 300fa74..81fad63 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -3,4 +3,6 @@ mod contract; #[cfg(test)] mod exec; #[cfg(test)] +mod type_id; +#[cfg(test)] mod util; diff --git a/test/src/type_id.rs b/test/src/type_id.rs new file mode 100644 index 0000000..11abb18 --- /dev/null +++ b/test/src/type_id.rs @@ -0,0 +1,183 @@ +use std::fs::File; +use std::io::Read; + +use ckb_hash::new_blake2b; +use ckb_testtool::ckb_types::{bytes::Bytes, core::TransactionBuilder, packed::*, prelude::*}; +use ckb_testtool::context::Context; +const MAX_CYCLES: u64 = 1000_0000; + +fn build_bins() -> (Bytes, Bytes) { + let always_success_bin = { + let mut buf = Vec::new(); + File::open("../target/riscv64imac-unknown-none-elf/debug/examples/main") + .unwrap() + .read_to_end(&mut buf) + .expect("read code"); + Bytes::from(buf) + }; + let type_id_bin = { + let mut buf = Vec::new(); + File::open("../target/riscv64imac-unknown-none-elf/debug/examples/type_id") + .unwrap() + .read_to_end(&mut buf) + .expect("read code"); + Bytes::from(buf) + }; + (always_success_bin, type_id_bin) +} + +fn type_id_mint(wrong_type_id: bool) { + let mut context = Context::default(); + let (always_success_bin, type_id_bin) = build_bins(); + let always_success_out_point = context.deploy_cell(always_success_bin); + let type_id_out_point = context.deploy_cell(type_id_bin); + let type_script_dep = CellDep::new_builder() + .out_point(type_id_out_point.clone()) + .build(); + + let lock_script = context + .build_script(&always_success_out_point, Default::default()) + .expect("script"); + let lock_script_dep = CellDep::new_builder() + .out_point(always_success_out_point) + .build(); + + let input_out_point = context.create_cell( + CellOutput::new_builder() + .capacity(1000u64.pack()) + .lock(lock_script.clone()) + .build(), + Bytes::new(), + ); + let input = CellInput::new_builder() + .previous_output(input_out_point) + .build(); + + let index: usize = 0; + let mut type_id = vec![0u8; 32]; + let mut blake2b = new_blake2b(); + blake2b.update(input.as_slice()); + blake2b.update(&index.to_be_bytes()); + blake2b.finalize(&mut type_id); + if wrong_type_id { + type_id[0] ^= 1; + } + let type_script = context + .build_script(&type_id_out_point, type_id.into()) + .expect("script"); + + let outputs = vec![CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script.clone()) + .type_(Some(type_script.clone()).pack()) + .build()]; + + let mut outputs_data: Vec = Vec::new(); + outputs_data.push(vec![42u8; 1000].into()); + + // build transaction + let tx = TransactionBuilder::default() + .input(input) + .outputs(outputs) + .outputs_data(outputs_data.pack()) + .cell_dep(lock_script_dep) + .cell_dep(type_script_dep) + .build(); + let tx = context.complete_tx(tx); + + // run + let result = context.verify_tx(&tx, MAX_CYCLES); + if wrong_type_id { + result.expect_err("should verify failed"); + } else { + result.expect("should verify success"); + } +} + +fn type_id_tx(wrong_type_id: bool) { + let mut context = Context::default(); + let (always_success_bin, type_id_bin) = build_bins(); + let always_success_out_point = context.deploy_cell(always_success_bin); + let type_id_out_point = context.deploy_cell(type_id_bin); + let type_script_dep = CellDep::new_builder() + .out_point(type_id_out_point.clone()) + .build(); + + let lock_script = context + .build_script(&always_success_out_point, Default::default()) + .expect("script"); + let lock_script_dep = CellDep::new_builder() + .out_point(always_success_out_point) + .build(); + let type_id = vec![1u8; 32]; + let type_script = context + .build_script(&type_id_out_point, type_id.into()) + .expect("script"); + + let input_out_point = context.create_cell( + CellOutput::new_builder() + .capacity(1000u64.pack()) + .lock(lock_script.clone()) + .type_(Some(type_script.clone()).pack()) + .build(), + Bytes::new(), + ); + let input = CellInput::new_builder() + .previous_output(input_out_point) + .build(); + + let type_id2 = if wrong_type_id { + vec![2u8; 32] + } else { + vec![1u8; 32] + }; + let type_script2 = context + .build_script(&type_id_out_point, type_id2.into()) + .expect("script"); + + let outputs = vec![CellOutput::new_builder() + .capacity(500u64.pack()) + .lock(lock_script.clone()) + .type_(Some(type_script2.clone()).pack()) + .build()]; + + let mut outputs_data: Vec = Vec::new(); + outputs_data.push(vec![42u8; 1000].into()); + + // build transaction + let tx = TransactionBuilder::default() + .input(input) + .outputs(outputs) + .outputs_data(outputs_data.pack()) + .cell_dep(lock_script_dep) + .cell_dep(type_script_dep) + .build(); + let tx = context.complete_tx(tx); + + // run + let result = context.verify_tx(&tx, MAX_CYCLES); + if wrong_type_id { + result.expect_err("should verify failed"); + } else { + result.expect("should verify success"); + } +} +#[test] +fn test_type_id_mint() { + type_id_mint(false); +} + +#[test] +fn test_type_id_mint_failed() { + type_id_mint(true); +} + +#[test] +fn test_type_id_tx() { + type_id_tx(false); +} + +#[test] +fn test_type_id_tx_failed() { + type_id_tx(true); +} From de5d7db564f98a3d48e96f7bece855b87793a28b Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 11:08:46 +0800 Subject: [PATCH 04/12] fix ci --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3361a4..d182873 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,4 +21,4 @@ jobs: - name: Push run: | cargo login ${{ secrets.CARGO_REGISTRY_TOKEN }} - cargo publish + make publish-crate From e81b45cc225bcc358212a50cde87e8b28f97e806 Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 11:17:15 +0800 Subject: [PATCH 05/12] fix Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index faa4a70..f23d349 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ CC := riscv64-unknown-elf-gcc default: integration publish-crate: - cargo publish --target ${TARGET} -p ckb-std + cargo publish --features build-with-clang --target ${TARGET} -p ckb-std publish-crate-dryrun: - cargo publish --dry-run --target ${TARGET} -p ckb-std --allow-dirty + cargo publish --dry-run --features build-with-clang --target ${TARGET} -p ckb-std --allow-dirty publish: publish-crate From 1fb11859b77fc11635239a17375449d70bf47603 Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 11:24:32 +0800 Subject: [PATCH 06/12] fix build script --- test/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Makefile b/test/Makefile index 9e56ffa..55a2180 100644 --- a/test/Makefile +++ b/test/Makefile @@ -9,8 +9,8 @@ build: cd ../contracts && RUSTFLAGS="-C target-feature=-a" cargo build --target riscv64imac-unknown-none-elf build-examples: - cd ../examples && RUSTFLAGS="-C target-feature=-a" cargo build --example type_id --target riscv64imac-unknown-none-elf --features "type-id" - cd ../examples && RUSTFLAGS="-C target-feature=-a" cargo build --example main --target riscv64imac-unknown-none-elf + cd ../examples && RUSTFLAGS="-C target-feature=-a" cargo build --features build-with-clang --example type_id --target riscv64imac-unknown-none-elf --features "type-id" + cd ../examples && RUSTFLAGS="-C target-feature=-a" cargo build --features build-with-clang --example main --target riscv64imac-unknown-none-elf clean: rm -rf ../build From f805465c47b02906a7420ec844be16caa63dccc2 Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 14:33:57 +0800 Subject: [PATCH 07/12] Remove cfg(std) from debug native simulator must have std enabled. --- src/debug.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/debug.rs b/src/debug.rs index e15e0d8..f78de62 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -47,11 +47,9 @@ macro_rules! debug { macro_rules! debug { ($fmt:literal) => { - #[cfg(std)] println!("{}", format!($fmt)); }; ($fmt:literal, $($args:expr),+) => { - #[cfg(std)] println!("{}", format!($fmt, $($args), +)); }; } From 8b4ac796d04dbb191d454714a0fcebf58e38880c Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 15:25:42 +0800 Subject: [PATCH 08/12] Update test dependencies --- contracts/ckb-std-tests/src/entry.rs | 3 ++- test/Cargo.toml | 8 ++++---- test/src/contract.rs | 3 ++- test/src/exec.rs | 3 ++- test/src/util.rs | 1 + 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/contracts/ckb-std-tests/src/entry.rs b/contracts/ckb-std-tests/src/entry.rs index 410f6a3..3715873 100644 --- a/contracts/ckb-std-tests/src/entry.rs +++ b/contracts/ckb-std-tests/src/entry.rs @@ -263,7 +263,8 @@ fn test_dynamic_loading_c_impl(context: &mut ContextType) { fn test_vm_version() { let version = syscalls::vm_version().unwrap(); debug!("vm version: {}", version); - assert_eq!(version, 1); + // currently, version 1(before hardfork) and 2(after hardfork) are both ok + assert!(version == 1 || version == 2); } fn test_current_cycles() { diff --git a/test/Cargo.toml b/test/Cargo.toml index 0aa6378..9e3a13a 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ckb-x64-simulator = "0.7" -ckb-testtool = "0.8.0" +ckb-x64-simulator = "0.9.2" +ckb-testtool = "0.13.1" serde_json = "1.0" -ckb-mock-tx-types = "0.4.0" +ckb-mock-tx-types = "0.118.0" blake2b-rs = "0.1.5" faster-hex = "0.6" -ckb-hash = "0.104.0" +ckb-hash = "0.118.0" diff --git a/test/src/contract.rs b/test/src/contract.rs index a7bbd2c..4df5393 100644 --- a/test/src/contract.rs +++ b/test/src/contract.rs @@ -1,7 +1,7 @@ use super::util::dump_mock_tx; use ckb_testtool::ckb_types::{bytes::Bytes, core::TransactionBuilder, packed::*, prelude::*}; use ckb_testtool::context::Context; -use ckb_x64_simulator::RunningSetup; +use ckb_x64_simulator::{RunningSetup, RunningType}; use std::collections::HashMap; use std::fs::File; use std::io::Read; @@ -86,6 +86,7 @@ fn it_works() { script_index: 0, vm_version: 1, native_binaries: HashMap::default(), + run_type: Some(RunningType::Executable), }; dump_mock_tx(test_case_name, &tx, &context, &setup); diff --git a/test/src/exec.rs b/test/src/exec.rs index 89ad2f1..f130ea9 100644 --- a/test/src/exec.rs +++ b/test/src/exec.rs @@ -9,7 +9,7 @@ use ckb_testtool::{ }, context::Context, }; -use ckb_x64_simulator::RunningSetup; +use ckb_x64_simulator::{RunningSetup, RunningType}; use std::collections::HashMap; use std::fs::File; use std::io::Read; @@ -161,6 +161,7 @@ fn test_exec_by_code_hash() { script_index: 0, vm_version: 1, native_binaries, + run_type: Some(RunningType::Executable), }; dump_mock_tx(test_case_name, &tx, &context, &setup); diff --git a/test/src/util.rs b/test/src/util.rs index 3d8067b..40c5203 100644 --- a/test/src/util.rs +++ b/test/src/util.rs @@ -64,6 +64,7 @@ fn build_mock_tx(tx: &core::TransactionView, context: &Context) -> MockTransacti inputs: mock_inputs, cell_deps: mock_cell_deps, header_deps: vec![], + extensions: Default::default(), }; MockTransaction { mock_info, From e38de270f67c50620e229d16593eab086a350848 Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 15:27:34 +0800 Subject: [PATCH 09/12] Convert usize to u64 --- src/type_id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/type_id.rs b/src/type_id.rs index 7fd8192..934b357 100644 --- a/src/type_id.rs +++ b/src/type_id.rs @@ -71,7 +71,7 @@ pub fn validate_type_id(type_id: [u8; 32]) -> Result<(), SysError> { // mint if !is_cell_present(0, Source::GroupInput) { - let index = locate_index()?; + let index = locate_index()? as u64; let input = load_input(0, Source::Input)?; let mut blake2b = new_blake2b(); blake2b.update(input.as_slice()); From 8e2e80f2d82b99af844de021f0ba123ec484f548 Mon Sep 17 00:00:00 2001 From: xjd Date: Mon, 23 Sep 2024 15:39:40 +0800 Subject: [PATCH 10/12] Add comments to validate_type_id --- src/type_id.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/type_id.rs b/src/type_id.rs index 934b357..aa1daf3 100644 --- a/src/type_id.rs +++ b/src/type_id.rs @@ -65,11 +65,15 @@ fn locate_index() -> Result { /// validate_type_id(type_id)?; /// ``` pub fn validate_type_id(type_id: [u8; 32]) -> Result<(), SysError> { + // after this checking, there are 3 cases: + // 1. 0 input cell and 1 output cell, it's minting operation + // 2. 1 input cell and 1 output cell, it's transfer operation + // 3. 1 input cell and 0 output cell, it's burning operation(allowed) if is_cell_present(1, Source::GroupInput) || is_cell_present(1, Source::GroupOutput) { return Err(SysError::TypeIDError); } - // mint + // case 1: minting operation if !is_cell_present(0, Source::GroupInput) { let index = locate_index()? as u64; let input = load_input(0, Source::Input)?; @@ -83,8 +87,7 @@ pub fn validate_type_id(type_id: [u8; 32]) -> Result<(), SysError> { return Err(SysError::TypeIDError); } } - // For the `else` part, Destroy an old cell with a specific type id and - // create a new cell with the same type id in the same transaction. + // case 2 & 3: for the `else` part, it's transfer operation or burning operation Ok(()) } From 4e2db0099e189fe1273fb39dcff7afdbfd557370 Mon Sep 17 00:00:00 2001 From: xjd Date: Tue, 24 Sep 2024 08:46:38 +0800 Subject: [PATCH 11/12] Remove unnecessary catch-all error handling --- contracts/ckb-std-tests/src/error.rs | 1 - contracts/exec-callee/src/error.rs | 1 - contracts/exec-caller-by-code-hash/src/error.rs | 1 - contracts/exec-caller/src/error.rs | 1 - contracts/spawn-callee/src/error.rs | 1 - contracts/spawn-caller-by-code-hash/src/error.rs | 1 - contracts/spawn-caller/src/error.rs | 1 - src/error.rs | 1 + test/simulator/src/exec_callee.rs | 1 - test/simulator/src/exec_caller_by_code_hash.rs | 1 - test/simulator/src/main.rs | 1 - 11 files changed, 1 insertion(+), 10 deletions(-) diff --git a/contracts/ckb-std-tests/src/error.rs b/contracts/ckb-std-tests/src/error.rs index 1285caf..c84bbe6 100644 --- a/contracts/ckb-std-tests/src/error.rs +++ b/contracts/ckb-std-tests/src/error.rs @@ -29,7 +29,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/contracts/exec-callee/src/error.rs b/contracts/exec-callee/src/error.rs index 1285caf..c84bbe6 100644 --- a/contracts/exec-callee/src/error.rs +++ b/contracts/exec-callee/src/error.rs @@ -29,7 +29,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/contracts/exec-caller-by-code-hash/src/error.rs b/contracts/exec-caller-by-code-hash/src/error.rs index 1285caf..c84bbe6 100644 --- a/contracts/exec-caller-by-code-hash/src/error.rs +++ b/contracts/exec-caller-by-code-hash/src/error.rs @@ -29,7 +29,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/contracts/exec-caller/src/error.rs b/contracts/exec-caller/src/error.rs index 1285caf..c84bbe6 100644 --- a/contracts/exec-caller/src/error.rs +++ b/contracts/exec-caller/src/error.rs @@ -29,7 +29,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/contracts/spawn-callee/src/error.rs b/contracts/spawn-callee/src/error.rs index 1ee871f..b78e033 100644 --- a/contracts/spawn-callee/src/error.rs +++ b/contracts/spawn-callee/src/error.rs @@ -28,7 +28,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/contracts/spawn-caller-by-code-hash/src/error.rs b/contracts/spawn-caller-by-code-hash/src/error.rs index 1ee871f..b78e033 100644 --- a/contracts/spawn-caller-by-code-hash/src/error.rs +++ b/contracts/spawn-caller-by-code-hash/src/error.rs @@ -28,7 +28,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/contracts/spawn-caller/src/error.rs b/contracts/spawn-caller/src/error.rs index 1ee871f..b78e033 100644 --- a/contracts/spawn-caller/src/error.rs +++ b/contracts/spawn-caller/src/error.rs @@ -28,7 +28,6 @@ impl From for Error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/src/error.rs b/src/error.rs index 10634a1..862383c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,6 +21,7 @@ pub enum SysError { /// Max fds has been spawned. Its value is 9. MaxFdsCreated, /// Type ID Error + #[cfg(feature = "type-id")] TypeIDError, /// Unknown syscall error number Unknown(u64), diff --git a/test/simulator/src/exec_callee.rs b/test/simulator/src/exec_callee.rs index 273f472..ff011ca 100644 --- a/test/simulator/src/exec_callee.rs +++ b/test/simulator/src/exec_callee.rs @@ -34,7 +34,6 @@ pub mod error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/test/simulator/src/exec_caller_by_code_hash.rs b/test/simulator/src/exec_caller_by_code_hash.rs index dea540d..28b899e 100644 --- a/test/simulator/src/exec_caller_by_code_hash.rs +++ b/test/simulator/src/exec_caller_by_code_hash.rs @@ -34,7 +34,6 @@ pub mod error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } diff --git a/test/simulator/src/main.rs b/test/simulator/src/main.rs index 6e7f80c..b91d7a9 100644 --- a/test/simulator/src/main.rs +++ b/test/simulator/src/main.rs @@ -37,7 +37,6 @@ pub mod error { MaxVmsSpawned => Self::MaxVmsSpawned, MaxFdsCreated => Self::MaxFdsCreated, Unknown(err_code) => panic!("unexpected sys error {}", err_code), - _ => panic!("other sys error"), } } } From a976b1a118f6529268d426929e9ec52ba66deffc Mon Sep 17 00:00:00 2001 From: xjd Date: Tue, 24 Sep 2024 11:28:03 +0800 Subject: [PATCH 12/12] Fix error in `make check` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f23d349..9b43297 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,6 @@ test: publish-crate-dryrun make -C test test check: - cargo check --target ${TARGET} --examples + cargo check --target ${TARGET} --examples --features type-id,build-with-clang .PHONY: test check