Skip to content

Commit 27041c7

Browse files
committed
Fix script locating bug when called via spawn / exec as a callee
1 parent 81f8b02 commit 27041c7

File tree

14 files changed

+521
-27
lines changed

14 files changed

+521
-27
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ resolver = "2"
44
members = [
55
"contracts/hybrid-sphincs-all-in-one-lock",
66
"contracts/sphincs-all-in-one-lock",
7+
"contracts/spawn-exec-test-runner",
78
"crates/ckb-fips205-utils",
89
"crates/fuzzing-corpus-generator",
910
"crates/intermediate-source-generator",

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ generate:
119119
--destination $(DESTINATION); \
120120
GENERATED_DIR=$$(ls -dt $(DESTINATION)/* | head -n 1); \
121121
if [ -f "$$GENERATED_DIR/.cargo-generate/tests.rs" ]; then \
122-
cat $$GENERATED_DIR/.cargo-generate/tests.rs >> tests/src/tests.rs; \
122+
# cat $$GENERATED_DIR/.cargo-generate/tests.rs >> tests/src/tests.rs; \
123123
rm -rf $$GENERATED_DIR/.cargo-generate/; \
124124
fi; \
125125
sed "s,@@INSERTION_POINT@@,@@INSERTION_POINT@@\n \"$$GENERATED_DIR\"\,," Cargo.toml > Cargo.toml.new; \
@@ -130,7 +130,7 @@ generate:
130130
--destination $(DESTINATION) \
131131
--name $(CRATE); \
132132
if [ -f "$(DESTINATION)/$(CRATE)/.cargo-generate/tests.rs" ]; then \
133-
cat $(DESTINATION)/$(CRATE)/.cargo-generate/tests.rs >> tests/src/tests.rs; \
133+
# cat $(DESTINATION)/$(CRATE)/.cargo-generate/tests.rs >> tests/src/tests.rs; \
134134
rm -rf $(DESTINATION)/$(CRATE)/.cargo-generate/; \
135135
fi; \
136136
sed '/@@INSERTION_POINT@@/s/$$/\n "$(DESTINATION)\/$(CRATE)",/' Cargo.toml > Cargo.toml.new; \

contracts/c-sphincs-all-in-one-lock/ckb-sphincsplus-root-lock.c

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,36 @@ int multisig_preliminary_check(WitnessArgsType *witness_args,
223223
int main(int argc, char *argv[]) {
224224
int err = CKB_SUCCESS;
225225

226-
/* Extract current script's data first, so we can reclaim buffer set aside
227-
for loading script to be used later */
228-
uint8_t code_hash[BLAKE2B_BLOCK_SIZE];
229-
uint8_t hash_type;
230226
uint8_t expected_multisig_hash[BLAKE2B_BLOCK_SIZE];
231-
CHECK(extract_script(code_hash, &hash_type, expected_multisig_hash));
227+
size_t cell_dep_index = (size_t)-1;
228+
{
229+
/* Extract current script's data first, so we can reclaim buffer set aside
230+
for loading script to be used later */
231+
uint8_t code_hash[BLAKE2B_BLOCK_SIZE];
232+
uint8_t hash_type;
233+
CHECK(extract_script(code_hash, &hash_type, expected_multisig_hash));
234+
235+
/*
236+
* 2 solutions exist to locate the cell dep containing quantum resistant
237+
* lock:
238+
*
239+
* * When current script is the entrypoint script, we will use run
240+
* ckb_look_for_dep_with_hash2 with current script's code_hash and
241+
* hash_type to figure the cell dep.
242+
* * When the quantum script is invoked via spawn or exec, we will expect
243+
* that `argv[0]` contains a 64-bit little endian cell dep index in zero
244+
* escaping.
245+
*/
246+
if (argc > 0) {
247+
size_t decoded_length = 0;
248+
CHECK2(zero_escape_decode_cstring_in_place(argv[0], &decoded_length) == 0,
249+
ERROR_SPHINCSPLUS_ARGV);
250+
CHECK2(decoded_length == 8, ERROR_SPHINCSPLUS_ARGV);
251+
cell_dep_index = *((size_t *)argv[0]);
252+
} else {
253+
CHECK(ckb_look_for_dep_with_hash2(code_hash, hash_type, &cell_dep_index));
254+
}
255+
}
232256

233257
/* The first witness in current script group contains signatures to validate
234258
*/
@@ -337,9 +361,11 @@ int main(int argc, char *argv[]) {
337361
.argv = (const char **)argv,
338362
.process_id = &spawned_processes[param_index],
339363
.inherited_fds = inherited_fds};
340-
CHECK(ckb_spawn_cell(
341-
code_hash, hash_type, *all_params[param_index]->offset_ptr,
342-
*all_params[param_index]->length_ptr, &spawn_args));
364+
size_t offset = *all_params[param_index]->offset_ptr;
365+
size_t length = *all_params[param_index]->length_ptr;
366+
size_t bounds = (offset << 32) | length;
367+
CHECK(ckb_spawn(cell_dep_index, CKB_SOURCE_CELL_DEP, 0, bounds,
368+
&spawn_args));
343369
}
344370
/* Sends data location to the leaf process */
345371
{
@@ -406,8 +432,11 @@ int main(int argc, char *argv[]) {
406432
ERROR_SPHINCSPLUS_ENCODING);
407433

408434
char *argv[] = {(char *)escaped};
409-
CHECK(ckb_exec_cell(code_hash, hash_type, *params->offset_ptr,
410-
*params->length_ptr, 1, (const char **)argv));
435+
size_t offset = *params->offset_ptr;
436+
size_t length = *params->length_ptr;
437+
size_t bounds = (offset << 32) | length;
438+
CHECK(ckb_exec(cell_dep_index, CKB_SOURCE_CELL_DEP, 0, bounds, 1,
439+
(const char **)argv));
411440
ckb_exit(ERROR_SPHINCSPLUS_UNEXPECTED);
412441
}
413442

contracts/hybrid-sphincs-all-in-one-lock/src/main.rs

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use ckb_std::{
2727
assert_eq,
2828
asserts::{expect_result, unwrap_option},
2929
ckb_constants::Source,
30+
env::argv,
3031
high_level, syscalls,
3132
};
3233
use core::ffi::CStr;
@@ -49,6 +50,7 @@ pub enum Error {
4950
PipeCreationFailure,
5051
SpawnFailure,
5152
IOFailure,
53+
Argv,
5254
}
5355

5456
impl From<Error> for i8 {
@@ -113,21 +115,28 @@ pub fn program_entry() -> i8 {
113115
&current_script.args().raw_data(),
114116
&actual_script_args_hash[..]
115117
);
116-
let cell_index = expect_result(
117-
Error::Syscall,
118-
high_level::look_for_dep_with_hash2(
119-
&current_script.code_hash().raw_data(),
120-
if current_script.hash_type().as_slice()[0]
121-
== core::convert::Into::<u8>::into(ScriptHashType::Type)
122-
{
123-
ScriptHashType::Type
124-
} else {
125-
// It does not really matter which dataN is used
126-
ScriptHashType::Data2
127-
},
128-
),
129-
"look for current script",
130-
);
118+
let cell_index = if let Some(argv0) = argv().first() {
119+
let mut argv0 = argv0.to_bytes().to_vec();
120+
let decoded = unwrap_option(Error::Argv, zero_decode_in_place(&mut argv0));
121+
assert_eq!(Error::Argv, decoded.len(), 8);
122+
u64::from_le_bytes(decoded.try_into().unwrap()) as usize
123+
} else {
124+
expect_result(
125+
Error::Syscall,
126+
high_level::look_for_dep_with_hash2(
127+
&current_script.code_hash().raw_data(),
128+
if current_script.hash_type().as_slice()[0]
129+
== core::convert::Into::<u8>::into(ScriptHashType::Type)
130+
{
131+
ScriptHashType::Type
132+
} else {
133+
// It does not really matter which dataN is used
134+
ScriptHashType::Data2
135+
},
136+
),
137+
"look for current script",
138+
)
139+
};
131140

132141
match parsed_param_id {
133142
ParsedParamId::Single(param_id) => {
@@ -285,6 +294,28 @@ pub fn program_entry() -> i8 {
285294
0
286295
}
287296

297+
pub fn zero_decode_in_place(buf: &mut [u8]) -> Option<&[u8]> {
298+
let mut wrote = 0;
299+
let mut i = 0;
300+
301+
while i < buf.len() {
302+
if buf[i] == 0xFE {
303+
if i + 1 >= buf.len() {
304+
return None;
305+
}
306+
buf[wrote] = buf[i + 1].wrapping_add(1);
307+
wrote += 1;
308+
i += 2;
309+
} else {
310+
buf[wrote] = buf[i];
311+
wrote += 1;
312+
i += 1;
313+
}
314+
}
315+
316+
Some(&buf[0..wrote])
317+
}
318+
288319
pub struct ZeroEncoder<'a> {
289320
i: usize,
290321
buf: &'a mut [u8],
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/build
2+
/target
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "spawn-exec-test-runner"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
ckb-std = "0.17.0"
8+
9+
[features]
10+
library = []
11+
native-simulator = ["library", "ckb-std/native-simulator"]
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# We cannot use $(shell pwd), which will return unix path format on Windows,
2+
# making it hard to use.
3+
cur_dir = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
4+
5+
TOP := $(cur_dir)
6+
# RUSTFLAGS that are likely to be tweaked by developers. For example,
7+
# while we enable debug logs by default here, some might want to strip them
8+
# for minimal code size / consumed cycles.
9+
CUSTOM_RUSTFLAGS := -C debug-assertions
10+
# RUSTFLAGS that are less likely to be tweaked by developers. Most likely
11+
# one would want to keep the default values here.
12+
FULL_RUSTFLAGS := -C target-feature=+zba,+zbb,+zbc,+zbs,-a $(CUSTOM_RUSTFLAGS)
13+
# Additional cargo args to append here. For example, one can use
14+
# make test CARGO_ARGS="-- --nocapture" so as to inspect data emitted to
15+
# stdout in unit tests
16+
CARGO_ARGS :=
17+
MODE := release
18+
# Tweak this to change the clang version to use for building C code. By default
19+
# we use a bash script with some heuristics to find clang in current system.
20+
CLANG := $(shell $(TOP)/scripts/find_clang)
21+
AR := $(subst clang,llvm-ar,$(CLANG))
22+
OBJCOPY := $(subst clang,llvm-objcopy,$(CLANG))
23+
# When this is set to some value, the generated binaries will be copied over
24+
BUILD_DIR :=
25+
# Generated binaries to copy. By convention, a Rust crate's directory name will
26+
# likely match the crate name, which is also the name of the final binary.
27+
# However if this is not the case, you can tweak this variable. As the name hints,
28+
# more than one binary is supported here.
29+
BINARIES := $(notdir $(shell pwd))
30+
31+
ifeq (release,$(MODE))
32+
MODE_ARGS := --release
33+
endif
34+
35+
default: build test
36+
37+
build:
38+
RUSTFLAGS="$(FULL_RUSTFLAGS)" TARGET_CC="$(CLANG)" TARGET_AR="$(AR)" \
39+
cargo build --target=riscv64imac-unknown-none-elf $(MODE_ARGS) $(CARGO_ARGS)
40+
@set -eu; \
41+
if [ "x$(BUILD_DIR)" != "x" ]; then \
42+
for binary in $(BINARIES); do \
43+
echo "Copying binary $$binary to build directory"; \
44+
cp $(TOP)/target/riscv64imac-unknown-none-elf/$(MODE)/$$binary $(TOP)/$(BUILD_DIR); \
45+
cp $(TOP)/$(BUILD_DIR)/$$binary $(TOP)/$(BUILD_DIR)/$$binary.debug; \
46+
$(OBJCOPY) --strip-debug --strip-all $(TOP)/$(BUILD_DIR)/$$binary; \
47+
done \
48+
fi
49+
50+
# test, check, clippy and fmt here are provided for completeness,
51+
# there is nothing wrong invoking cargo directly instead of make.
52+
test:
53+
cargo test $(CARGO_ARGS)
54+
55+
check:
56+
cargo check $(CARGO_ARGS)
57+
58+
clippy:
59+
cargo clippy $(CARGO_ARGS)
60+
61+
fmt:
62+
cargo fmt $(CARGO_ARGS)
63+
64+
# Arbitrary cargo command is supported here. For example:
65+
#
66+
# make cargo CARGO_CMD=expand CARGO_ARGS="--ugly"
67+
#
68+
# Invokes:
69+
# cargo expand --ugly
70+
CARGO_CMD :=
71+
cargo:
72+
cargo $(CARGO_CMD) $(CARGO_ARGS)
73+
74+
clean:
75+
cargo clean
76+
77+
prepare:
78+
rustup target add riscv64imac-unknown-none-elf
79+
80+
.PHONY: build test check clippy fmt cargo clean prepare
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# spawn-exec-test-runner
2+
3+
An example calling quantum resistant lock via spawn / exec syscall. It serves both as an example, and as a test runner for the quantum resistant lock.
4+
5+
The example script here always assume that the quantum resistant lock resides at cell dep index 1 of current script. However in a more complicated case, a ckb-std [API](https://docs.rs/ckb-std/latest/ckb_std/high_level/fn.look_for_dep_with_hash2.html) is likely to be used to locate the actual cell dep index for the quantum resistant lock.
6+
7+
*This contract was bootstrapped with [ckb-script-templates].*
8+
9+
[ckb-script-templates]: https://github.com/cryptape/ckb-script-templates
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![cfg_attr(not(feature = "library"), no_std)]
2+
#![allow(special_module_name)]
3+
#![allow(unused_attributes)]
4+
#[cfg(feature = "library")]
5+
mod main;
6+
#[cfg(feature = "library")]
7+
pub use main::program_entry;
8+
9+
extern crate alloc;

0 commit comments

Comments
 (0)