Skip to content

Commit

Permalink
Merge pull request #17 from TheWaWaR/add-ckb2021-syscalls
Browse files Browse the repository at this point in the history
feat: add ckb2021 syscalls: vm_version, current_cycles, exec
  • Loading branch information
TheWaWaR committed Sep 18, 2021
2 parents 3939759 + 0361d47 commit 379d4a2
Show file tree
Hide file tree
Showing 19 changed files with 435 additions and 22 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ckb-std"
version = "0.8.0"
version = "0.9.0"
authors = ["Nervos network"]
edition = "2018"
license = "MIT"
Expand All @@ -19,6 +19,7 @@ simulator = [ "ckb-x64-simulator" ]
cc = "1.0"

[dependencies]
ckb-types = { package = "ckb-standalone-types", version = "0.1.1", default-features = false, optional = true }
cstr_core = { version = "0.2.4", default-features = false, features = ["alloc"] }
ckb-types = { package = "ckb-standalone-types", version = "0.1.2", default-features = false, optional = true }
buddy-alloc = { version = "0.4.1", optional = true }
ckb-x64-simulator = { version = "0.5.0", optional = true }
1 change: 1 addition & 0 deletions examples/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(lang_items)]
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]
Expand Down
3 changes: 3 additions & 0 deletions src/ckb_constants.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
pub const SYS_EXIT: u64 = 93;
pub const SYS_VM_VERSION: u64 = 2041;
pub const SYS_CURRENT_CYCLES: u64 = 2042;
pub const SYS_EXEC: u64 = 2043;
pub const SYS_LOAD_TRANSACTION: u64 = 2051;
pub const SYS_LOAD_SCRIPT: u64 = 2052;
pub const SYS_LOAD_TX_HASH: u64 = 2061;
Expand Down
30 changes: 28 additions & 2 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,34 @@ macro_rules! entry {

#[no_mangle]
pub extern "C" fn _start() -> ! {
let f: fn() -> i8 = $main;
$crate::syscalls::exit(f())
#[cfg(target_arch = "riscv64")]
unsafe {
asm!(
"lw a0, 0(sp)",
"addi a1, sp, 8",
"li a2, 0",
"call {}",
"li a7, 93",
"ecall",
sym $main,
options(noreturn)
);
}
#[cfg(not(target_arch = "riscv64"))]
unsafe {
asm!(
"mov rsp, rdi",
"mov rsp, rsi",
"add 8, rsi",
"mov 0 rdx",
"call {}",
"mov rax rdi",
"mov 60 rax",
"syscall",
sym $main,
options(noreturn)
);
}
}

#[lang = "eh_personality"]
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl SysError {
if actual_data_len > load_len {
return Err(LengthNotEnough(actual_data_len));
}
return Ok(actual_data_len);
Ok(actual_data_len)
}
1 => Err(IndexOutOfBound),
2 => Err(ItemMissing),
Expand Down
63 changes: 59 additions & 4 deletions src/high_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use crate::ckb_constants::*;
use crate::debug;
use crate::error::SysError;
use crate::syscalls;
use alloc::vec::Vec;
use ckb_types::{packed::*, prelude::*};
use alloc::{vec, vec::Vec};
use ckb_types::{core::ScriptHashType, packed::*, prelude::*};
use cstr_core::CStr;

/// Default buffer size
pub const BUF_SIZE: usize = 1024;
Expand Down Expand Up @@ -48,8 +49,7 @@ fn load_data<F: Fn(&mut [u8], usize) -> Result<usize, SysError>>(
match syscall(&mut buf, 0) {
Ok(len) => Ok(buf[..len].to_vec()),
Err(SysError::LengthNotEnough(actual_size)) => {
let mut data = Vec::with_capacity(actual_size);
data.resize(actual_size, 0);
let mut data = vec![0; actual_size];
let loaded_len = buf.len();
data[..loaded_len].copy_from_slice(&buf);
let len = syscall(&mut data[loaded_len..], loaded_len)?;
Expand Down Expand Up @@ -554,3 +554,58 @@ pub fn find_cell_by_data_hash(data_hash: &[u8], source: Source) -> Result<Option
}
Ok(None)
}

/// Look for a dep cell with specific code hash, code_hash should be a buffer
/// with 32 bytes.
///
pub fn look_for_dep_with_hash2(
code_hash: &[u8],
hash_type: ScriptHashType,
) -> Result<usize, SysError> {
let field = match hash_type {
ScriptHashType::Type => CellField::TypeHash,
_ => CellField::DataHash,
};
let mut current: usize = 0;
loop {
let mut buf = [0u8; 32];
match syscalls::load_cell_by_field(&mut buf, 0, current, Source::CellDep, field) {
Ok(len) => {
debug_assert_eq!(len, buf.len());
if buf == code_hash {
return Ok(current);
}
}
Err(SysError::ItemMissing) => {}
Err(SysError::IndexOutOfBound) => {
return Err(SysError::IndexOutOfBound);
}
Err(err) => {
return Err(err);
}
}
current += 1;
}
}

pub fn look_for_dep_with_data_hash(data_hash: &[u8]) -> Result<usize, SysError> {
look_for_dep_with_hash2(data_hash, ScriptHashType::Data)
}

/// Exec a cell in cell dep.
///
/// # Arguments
///
/// * `code_hash` - the code hash to search cell in cell deps.
/// * `hash_type` - the hash type to search cell in cell deps.
pub fn exec_cell(
code_hash: &[u8],
hash_type: ScriptHashType,
offset: u32,
length: u32,
argv: &[&CStr],
) -> Result<u64, SysError> {
let index = look_for_dep_with_hash2(code_hash, hash_type)?;
let bounds: usize = (offset as usize) << 32 | (length as usize);
Ok(syscalls::exec(index, Source::CellDep, 0, bounds, argv))
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ pub mod dynamic_loading;
pub mod dynamic_loading_c_impl;
#[cfg(feature = "allocator")]
pub use buddy_alloc;
pub use cstr_core;
85 changes: 83 additions & 2 deletions src/syscalls/native.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{ckb_constants::*, error::SysError};
use cstr_core::CStr;

#[cfg(target_arch = "riscv64")]
#[link(name = "ckb-syscall")]
Expand All @@ -7,8 +8,17 @@ extern "C" {
}

#[cfg(not(target_arch = "riscv64"))]
fn syscall(a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64) -> u64 {
return u64::MAX;
unsafe fn syscall(
_a0: u64,
_a1: u64,
_a2: u64,
_a3: u64,
_a4: u64,
_a5: u64,
_a6: u64,
_a7: u64,
) -> u64 {
u64::MAX
}

/// Exit, this script will be terminated after the exit syscall.
Expand Down Expand Up @@ -480,3 +490,74 @@ pub fn load_cell_code(
};
SysError::build_syscall_result(ret, len, len)
}

/// *VM version* syscall returns current running VM version, so far 2 values will be returned:
/// - Error for Lina CKB-VM version
/// - 1 for the new hardfork CKB-VM version.
///
/// This syscall consumes 500 cycles.
pub fn vm_version() -> Result<u64, SysError> {
let ret = unsafe { syscall(0, 0, 0, 0, 0, 0, 0, SYS_VM_VERSION) };
if ret == 1 {
Ok(1)
} else {
Err(SysError::Unknown(ret))
}
}

/// *Current Cycles* returns current cycle consumption just before executing this syscall.
/// This syscall consumes 500 cycles.
pub fn current_cycles() -> u64 {
unsafe { syscall(0, 0, 0, 0, 0, 0, 0, SYS_CURRENT_CYCLES) }
}

/// Exec runs an executable file from specified cell data in the context of an
/// already existing machine, replacing the previous executable. The used cycles
/// does not change, but the code, registers and memory of the vm are replaced
/// by those of the new program. It's cycles consumption consists of two parts:
///
/// - Fixed 500 cycles
/// - Initial Loading Cycles (https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0014-vm-cycle-limits/0014-vm-cycle-limits.md)
///
/// The arguments used here are:
///
/// * `index`: an index value denoting the index of entries to read.
/// * `source`: a flag denoting the source of cells or witnesses to locate, possible values include:
/// + 1: input cells.
/// + `0x0100000000000001`: input cells with the same running script as current script
/// + 2: output cells.
/// + `0x0100000000000002`: output cells with the same running script as current script
/// + 3: dep cells.
/// * `place`: A value of 0 or 1:
/// + 0: read from cell data
/// + 1: read from witness
/// * `bounds`: high 32 bits means `offset`, low 32 bits means `length`. if `length` equals to zero, it read to end instead of reading 0 bytes.
/// * `argc`: argc contains the number of arguments passed to the program
/// * `argv`: argv is a one-dimensional array of strings
pub fn exec(
index: usize,
source: Source,
place: usize,
bounds: usize,
// argc: i32,
argv: &[&CStr],
) -> u64 {
// https://www.gnu.org/software/libc/manual/html_node/Program-Arguments.html
let argc = argv.len();
let mut argv_ptr = alloc::vec![core::ptr::null(); argc + 1];
for (idx, cstr) in argv.into_iter().enumerate() {
argv_ptr[idx] = cstr.as_ptr();
}
unsafe {
syscall(
index as u64,
source as u64,
place as u64,
bounds as u64,
argc as u64,
argv_ptr.as_ptr() as u64,
0,
SYS_EXEC,
)
}
}
2 changes: 1 addition & 1 deletion test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ckb-testtool = "0.5"
ckb-testtool = "0.6.1"
4 changes: 4 additions & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ build:
cd contract && cargo build

C := contract/target/riscv64imac-unknown-none-elf/debug/contract
EXEC_CALLER := contract/target/riscv64imac-unknown-none-elf/debug/exec-caller
EXEC_CALLEE := contract/target/riscv64imac-unknown-none-elf/debug/exec-callee
patch:
ckb-binary-patcher -i $C -o $C
ckb-binary-patcher -i ${EXEC_CALLER} -o ${EXEC_CALLER}
ckb-binary-patcher -i ${EXEC_CALLEE} -o ${EXEC_CALLEE}

clean:
cd contract && cargo clean
12 changes: 12 additions & 0 deletions test/contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ authors = ["Nervos network"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "contract"
path = "src/main.rs"
[[bin]]
name = "exec-caller"
path = "src/exec_caller.rs"
[[bin]]
name = "exec-caller-by-code-hash"
path = "src/exec_caller_by_code_hash.rs"
[[bin]]
name = "exec-callee"
path = "src/exec_callee.rs"

[dependencies]
ckb-std = { path = "../.." }
Expand Down
3 changes: 1 addition & 2 deletions test/contract/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,11 @@ fn main() {
format!("CODE_HASH_{}", name.to_uppercase().replace("-", "_")),
hash
)
.expect("write to code_hashes.rs");
.expect("write to code_hashes.rs");
}

pub fn new_blake2b() -> Blake2b {
Blake2bBuilder::new(32)
.personal(CKB_HASH_PERSONALIZATION)
.build()
}

27 changes: 27 additions & 0 deletions test/contract/src/exec_callee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(lang_items)]
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]

use ckb_std::{
cstr_core::{cstr, CStr},
default_alloc, entry,
};

use core::slice::from_raw_parts;

#[no_mangle]
pub fn main(argc: u64, argv: *const *const u8) -> i8 {
let args = unsafe { from_raw_parts(argv, argc as usize) };
let arg1 = unsafe { CStr::from_ptr(args[0]) };
let arg2 = unsafe { CStr::from_ptr(args[1]) };
assert_eq!(argc, 2);
assert_eq!(arg1, cstr!("Hello World"));
assert_eq!(arg2, cstr!("你好"));
0
}

entry!(main);
default_alloc!();
19 changes: 19 additions & 0 deletions test/contract/src/exec_caller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(lang_items)]
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]

use ckb_std::{ckb_constants::*, cstr_core::cstr, default_alloc, entry, syscalls};

#[no_mangle]
pub fn main() -> i8 {
let arg1 = cstr!("Hello World");
let arg2 = cstr!("你好");
let ret = syscalls::exec(0, Source::CellDep, 0, 0, &[arg1, arg2][..]);
panic!("exec failed: {}", ret);
}

entry!(main);
default_alloc!();
33 changes: 33 additions & 0 deletions test/contract/src/exec_caller_by_code_hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(lang_items)]
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]

use ckb_std::{ckb_types::core::ScriptHashType, cstr_core::cstr, default_alloc, entry, high_level};

#[no_mangle]
pub fn main() -> i8 {
let arg1 = cstr!("Hello World");
let arg2 = cstr!("你好");
// $ ckb-cli util blake2b --binary-path target/riscv64imac-unknown-none-elf/debug/exec-callee
// 0x6c704a9d5084cd52ff3a0be1f785b5f0fb9663092f8083bef7fc3f547758cb5a
let code_hash = [
0x6c, 0x70, 0x4a, 0x9d, 0x50, 0x84, 0xcd, 0x52, 0xff, 0x3a, 0x0b, 0xe1, 0xf7, 0x85, 0xb5,
0xf0, 0xfb, 0x96, 0x63, 0x09, 0x2f, 0x80, 0x83, 0xbe, 0xf7, 0xfc, 0x3f, 0x54, 0x77, 0x58,
0xcb, 0x5a,
];
let ret = high_level::exec_cell(
&code_hash[..],
ScriptHashType::Data1,
0,
0,
&[arg1, arg2][..],
)
.unwrap();
panic!("exec failed: {}", ret);
}

entry!(main);
default_alloc!();
Loading

0 comments on commit 379d4a2

Please sign in to comment.