@@ -236,6 +236,8 @@ fn is_v014_runtime_helper(func: &str) -> bool {
236236 | "__ckb_witness_lock"
237237 | "__ckb_witness_input_type"
238238 | "__ckb_witness_output_type"
239+ | "__ckb_witness_size"
240+ | "__ckb_require_witness_size_at_least"
239241 | "__ckb_sighash_all"
240242 | "__ckb_require_maturity"
241243 | "__ckb_require_time"
@@ -9723,6 +9725,8 @@ impl CodeGenerator {
97239725 ("__ckb_witness_lock", "WitnessArgs.lock"),
97249726 ("__ckb_witness_input_type", "WitnessArgs.input_type"),
97259727 ("__ckb_witness_output_type", "WitnessArgs.output_type"),
9728+ ("__ckb_witness_size", "witness byte size"),
9729+ ("__ckb_require_witness_size_at_least", "require witness size lower bound"),
97269730 ("__ckb_sighash_all", "CKB sighash-all digest"),
97279731 ("__ckb_require_maturity", "CKB block-number since maturity"),
97289732 ("__ckb_require_time", "CKB timestamp since"),
@@ -9842,6 +9846,14 @@ impl CodeGenerator {
98429846 "__xudt_require_group_amount_burned" => {
98439847 self.emit_runtime_xudt_require_group_amount_delta_helper(name, false, enabled);
98449848 }
9849+ "__ckb_witness_size" => self.emit_runtime_witness_size_helper(enabled),
9850+ "__ckb_require_witness_size_at_least" => {
9851+ self.emit_runtime_require_witness_size_at_least_helper(enabled)
9852+ }
9853+ "__ckb_witness_raw" => self.emit_runtime_witness_raw_helper(enabled),
9854+ "__ckb_witness_lock" => self.emit_runtime_witness_args_field_helper(name, detail, 0, enabled),
9855+ "__ckb_witness_input_type" => self.emit_runtime_witness_args_field_helper(name, detail, 1, enabled),
9856+ "__ckb_witness_output_type" => self.emit_runtime_witness_args_field_helper(name, detail, 2, enabled),
98459857 _ => {
98469858 self.emit_global(name);
98479859 self.emit_label(name);
@@ -9857,6 +9869,345 @@ impl CodeGenerator {
98579869 }
98589870 }
98599871
9872+ fn emit_runtime_witness_size_helper(&mut self, enabled: bool) {
9873+ const SIZE_OFFSET: usize = 8;
9874+ const RA_OFFSET: usize = 24;
9875+ const FRAME_SIZE: usize = 32;
9876+
9877+ self.emit_global("__ckb_witness_size");
9878+ self.emit_label("__ckb_witness_size");
9879+ self.emit("# cellscript abi: witness byte size via LOAD_WITNESS");
9880+ self.emit("# cellscript abi: args a0=SourceView; returns a0=status, a1=size");
9881+ if !enabled {
9882+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
9883+ self.emit("ret");
9884+ return;
9885+ }
9886+ let invalid = self.fresh_label("witness_size_source_invalid");
9887+ let failed = self.fresh_label("witness_size_load_failed");
9888+ let done = self.fresh_label("witness_size_done");
9889+ let abi = self.runtime_abi();
9890+
9891+ self.emit(format!("addi sp, sp, -{}", FRAME_SIZE));
9892+ self.emit(format!("sd ra, {}(sp)", RA_OFFSET));
9893+ self.emit_decode_source_view_to_t1_t2(&invalid);
9894+ self.emit("li t0, 0");
9895+ self.emit(format!("sd t0, {}(sp)", SIZE_OFFSET));
9896+ self.emit("li a0, 0");
9897+ self.emit(format!("addi a1, sp, {}", SIZE_OFFSET));
9898+ self.emit("li a2, 0");
9899+ self.emit("addi a3, t1, 0");
9900+ self.emit("addi a4, t2, 0");
9901+ self.emit(format!("li a7, {}", abi.load_witness));
9902+ self.emit("ecall");
9903+ self.emit(format!("beqz a0, {}", done));
9904+ self.emit(format!("j {}", failed));
9905+
9906+ self.emit_label(&invalid);
9907+ self.emit(format!("li a0, {}", CellScriptRuntimeError::CkbSourceViewInvalid.code()));
9908+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
9909+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
9910+ self.emit("ret");
9911+
9912+ self.emit_label(&failed);
9913+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
9914+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
9915+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
9916+ self.emit("ret");
9917+
9918+ self.emit_label(&done);
9919+ self.emit("li a0, 0");
9920+ self.emit(format!("ld a1, {}(sp)", SIZE_OFFSET));
9921+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
9922+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
9923+ self.emit("ret");
9924+ }
9925+
9926+ fn emit_runtime_require_witness_size_at_least_helper(&mut self, enabled: bool) {
9927+ const SIZE_OFFSET: usize = 8;
9928+ const RA_OFFSET: usize = 24;
9929+ const FRAME_SIZE: usize = 32;
9930+
9931+ self.emit_global("__ckb_require_witness_size_at_least");
9932+ self.emit_label("__ckb_require_witness_size_at_least");
9933+ self.emit("# cellscript abi: require witness size >= min_size");
9934+ self.emit("# cellscript abi: args a0=SourceView, a1=min_size; returns a0=status");
9935+ if !enabled {
9936+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
9937+ self.emit("ret");
9938+ return;
9939+ }
9940+ let invalid = self.fresh_label("witness_req_size_source_invalid");
9941+ let failed = self.fresh_label("witness_req_size_load_failed");
9942+ let too_small = self.fresh_label("witness_req_size_too_small");
9943+ let ok = self.fresh_label("witness_req_size_ok");
9944+ let done = self.fresh_label("witness_req_size_done");
9945+ let abi = self.runtime_abi();
9946+
9947+ self.emit(format!("addi sp, sp, -{}", FRAME_SIZE));
9948+ self.emit(format!("sd ra, {}(sp)", RA_OFFSET));
9949+ self.emit_decode_source_view_to_t1_t2(&invalid);
9950+ self.emit("li t0, 0");
9951+ self.emit(format!("sd t0, {}(sp)", SIZE_OFFSET));
9952+ self.emit("li a0, 0");
9953+ self.emit(format!("addi a1, sp, {}", SIZE_OFFSET));
9954+ self.emit("li a2, 0");
9955+ self.emit("addi a3, t1, 0");
9956+ self.emit("addi a4, t2, 0");
9957+ self.emit(format!("li a7, {}", abi.load_witness));
9958+ self.emit("ecall");
9959+ self.emit(format!("beqz a0, {}", ok));
9960+ self.emit(format!("j {}", failed));
9961+
9962+ self.emit_label(&ok);
9963+ self.emit(format!("ld t0, {}(sp)", SIZE_OFFSET));
9964+ self.emit("addi t1, a1, 0");
9965+ self.emit("sltu t2, t0, t1");
9966+ self.emit(format!("beqz t2, {}", done));
9967+
9968+ self.emit_label(&too_small);
9969+ self.emit_epilogue();
9970+
9971+ self.emit_label(&invalid);
9972+ self.emit(format!("li a0, {}", CellScriptRuntimeError::CkbSourceViewInvalid.code()));
9973+ self.emit("ret");
9974+
9975+ self.emit_label(&failed);
9976+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
9977+ self.emit("ret");
9978+
9979+ self.emit_label(&done);
9980+ self.emit("li a0, 0");
9981+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
9982+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
9983+ self.emit("ret");
9984+ }
9985+
9986+ fn emit_runtime_witness_raw_helper(&mut self, enabled: bool) {
9987+ const SIZE_OFFSET: usize = 8;
9988+ const BUFFER_OFFSET: usize = 16;
9989+ const RA_OFFSET: usize = 56;
9990+ const FRAME_SIZE: usize = 64;
9991+
9992+ self.emit_global("__ckb_witness_raw");
9993+ self.emit_label("__ckb_witness_raw");
9994+ self.emit("# cellscript abi: load raw witness bytes as a 32-byte Hash");
9995+ self.emit("# cellscript abi: args a0=out32_ptr, a1=size_ptr; returns a0=status");
9996+ if !enabled {
9997+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
9998+ self.emit("ret");
9999+ return;
10000+ }
10001+ let invalid = self.fresh_label("witness_raw_source_invalid");
10002+ let failed = self.fresh_label("witness_raw_load_failed");
10003+ let done = self.fresh_label("witness_raw_done");
10004+ let abi = self.runtime_abi();
10005+
10006+ self.emit(format!("addi sp, sp, -{}", FRAME_SIZE));
10007+ self.emit(format!("sd ra, {}(sp)", RA_OFFSET));
10008+ self.emit_decode_source_view_to_t1_t2(&invalid);
10009+ self.emit("li t0, 32");
10010+ self.emit(format!("sd t0, {}(sp)", SIZE_OFFSET));
10011+ self.emit(format!("addi a0, sp, {}", BUFFER_OFFSET));
10012+ self.emit(format!("addi a1, sp, {}", SIZE_OFFSET));
10013+ self.emit("li a2, 0");
10014+ self.emit("addi a3, t1, 0");
10015+ self.emit("addi a4, t2, 0");
10016+ self.emit(format!("li a7, {}", abi.load_witness));
10017+ self.emit("ecall");
10018+ self.emit(format!("beqz a0, {}", done));
10019+ self.emit(format!("j {}", failed));
10020+
10021+ self.emit_label(&invalid);
10022+ self.emit(format!("li a0, {}", CellScriptRuntimeError::CkbSourceViewInvalid.code()));
10023+ self.emit("ret");
10024+
10025+ self.emit_label(&failed);
10026+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
10027+ self.emit("ret");
10028+
10029+ self.emit_label(&done);
10030+ self.emit("li a0, 0");
10031+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
10032+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
10033+ self.emit("ret");
10034+ }
10035+
10036+ fn emit_runtime_witness_args_field_helper(&mut self, symbol: &str, detail: &str, field_index: u64, enabled: bool) {
10037+ const SIZE_OFFSET: usize = 8;
10038+ const FULL_BUFFER_OFFSET: usize = 16;
10039+ const FULL_BUFFER_SIZE: usize = 512;
10040+ const FIELD_SIZE_OFFSET: usize = SIZE_OFFSET + FULL_BUFFER_SIZE;
10041+ const FIELD_BUF_OFFSET: usize = FIELD_SIZE_OFFSET + 8;
10042+ const FIELD_BUF_SIZE: usize = 128;
10043+ const HEADER_READ_OFFSET: usize = FIELD_BUF_OFFSET + FIELD_BUF_SIZE;
10044+ const RA_OFFSET: usize = HEADER_READ_OFFSET + 8;
10045+ const FRAME_SIZE: usize = RA_OFFSET + 8;
10046+
10047+ self.emit_global(symbol);
10048+ self.emit_label(symbol);
10049+ self.emit(format!("# cellscript abi: extract WitnessArgs field {} ({})", field_index, detail));
10050+ self.emit("# cellscript abi: args a0=out32_ptr, a1=size_ptr; returns a0=status");
10051+ if !enabled {
10052+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
10053+ self.emit("ret");
10054+ return;
10055+ }
10056+ let invalid = self.fresh_label("witness_field_source_invalid");
10057+ let failed = self.fresh_label("witness_field_load_failed");
10058+ let malformed = self.fresh_label("witness_field_malformed");
10059+ let truncated = self.fresh_label("witness_field_truncated");
10060+ let field_absent = self.fresh_label("witness_field_absent");
10061+ let ok = self.fresh_label("witness_field_ok");
10062+ let done = self.fresh_label("witness_field_done");
10063+ let abi = self.runtime_abi();
10064+
10065+ self.emit(format!("addi sp, sp, -{}", FRAME_SIZE));
10066+ self.emit(format!("sd ra, {}(sp)", RA_OFFSET));
10067+ self.emit_decode_source_view_to_t1_t2(&invalid);
10068+
10069+ // Load full witness
10070+ self.emit(format!("li t0, {}", FULL_BUFFER_SIZE));
10071+ self.emit(format!("sd t0, {}(sp)", SIZE_OFFSET));
10072+ self.emit(format!("addi a0, sp, {}", FULL_BUFFER_OFFSET));
10073+ self.emit(format!("addi a1, sp, {}", SIZE_OFFSET));
10074+ self.emit("li a2, 0");
10075+ self.emit("addi a3, t1, 0");
10076+ self.emit("addi a4, t2, 0");
10077+ self.emit(format!("li a7, {}", abi.load_witness));
10078+ self.emit("ecall");
10079+ self.emit(format!("beqz a0, {}", ok));
10080+ self.emit(format!("j {}", failed));
10081+
10082+ self.emit_label(&ok);
10083+ self.emit(format!("ld t0, {}(sp)", SIZE_OFFSET));
10084+
10085+ // Parse Molecule WitnessArgs table header (20 bytes minimum)
10086+ self.emit("li t1, 20");
10087+ self.emit("sltu t2, t0, t1");
10088+ self.emit(format!("bnez t2, {}", malformed));
10089+
10090+ // Read field_count at offset 4
10091+ self.emit(format!("addi t3, sp, {}", FULL_BUFFER_OFFSET));
10092+ self.emit("lwu t4, 4(t3)");
10093+ self.emit("li t5, 3");
10094+ self.emit("sub t6, t4, t5");
10095+ self.emit(format!("bnez t6, {}", malformed));
10096+
10097+ // Read field offsets from header (offsets 8, 12, 16)
10098+ self.emit("lwu t4, 8(t3)");
10099+ self.emit(format!("sd t4, {}(sp)", HEADER_READ_OFFSET));
10100+ self.emit("lwu t5, 12(t3)");
10101+ self.emit(format!("sd t5, {}(sp)", HEADER_READ_OFFSET + 8));
10102+ self.emit("lwu t6, 16(t3)");
10103+ self.emit(format!("sd t6, {}(sp)", HEADER_READ_OFFSET + 16));
10104+
10105+ // Select field offset and next field offset
10106+ let field_offsets_offset = HEADER_READ_OFFSET + (field_index * 8) as usize;
10107+ let next_offsets_offset = HEADER_READ_OFFSET + ((field_index + 1) * 8) as usize;
10108+ self.emit(format!("ld t4, {}(sp)", field_offsets_offset));
10109+ if field_index < 2 {
10110+ self.emit(format!("ld t5, {}(sp)", next_offsets_offset));
10111+ } else {
10112+ self.emit(format!("addi t5, t0, 0"));
10113+ }
10114+
10115+ // Check field offset bounds: field_offset < next_offset <= total_size
10116+ self.emit("sltu t2, t4, t5");
10117+ self.emit(format!("beqz t2, {}", malformed));
10118+ self.emit("sltu t2, t0, t5");
10119+ self.emit(format!("bnez t2, {}", truncated));
10120+
10121+ // Calculate field data size: next_offset - field_offset - 4 (BytesOpt length prefix)
10122+ self.emit("sub t2, t5, t4");
10123+ self.emit("li t6, 4");
10124+ self.emit("sltu t3, t2, t6");
10125+ self.emit(format!("bnez t3, {}", malformed));
10126+ self.emit("addi t2, t2, -4");
10127+
10128+ // Read BytesOpt length at field_offset
10129+ self.emit(format!("addi t3, sp, {}", FULL_BUFFER_OFFSET));
10130+ self.emit("add t6, t3, t4");
10131+ self.emit("lwu t1, 0(t6)");
10132+ self.emit(format!("beqz t1, {}", field_absent));
10133+
10134+ // Check length <= field data size
10135+ self.emit("sltu t3, t2, t1");
10136+ self.emit(format!("bnez t3, {}", malformed));
10137+
10138+ // Copy field bytes to output buffer (max 32 bytes for Hash)
10139+ self.emit("li t3, 32");
10140+ self.emit("sltu t5, t3, t1");
10141+ self.emit("bnez t5, 1f");
10142+ self.emit("addi t1, t3, 0");
10143+ self.emit("1:");
10144+ self.emit(format!("addi t2, sp, {}", FIELD_BUF_OFFSET));
10145+ self.emit("addi t4, t6, 4");
10146+ // Copy loop
10147+ self.emit("li t3, 0");
10148+ let copy_loop = self.fresh_label("witness_field_copy_loop");
10149+ let copy_done = self.fresh_label("witness_field_copy_done");
10150+ self.emit_label(©_loop);
10151+ self.emit("sltu t5, t3, t1");
10152+ self.emit(format!("beqz t5, {}", copy_done));
10153+ self.emit("add t5, t4, t3");
10154+ self.emit("lbu t6, 0(t5)");
10155+ self.emit("add t5, t2, t3");
10156+ self.emit("sb t6, 0(t5)");
10157+ self.emit("addi t3, t3, 1");
10158+ self.emit(format!("j {}", copy_loop));
10159+ self.emit_label(©_done);
10160+ self.emit(format!("j {}", done));
10161+
10162+ self.emit_label(&field_absent);
10163+ self.emit("li t1, 0");
10164+ self.emit("li t3, 32");
10165+ self.emit(format!("addi t2, sp, {}", FIELD_BUF_OFFSET));
10166+ // Zero-fill for absent field
10167+ self.emit("li t4, 0");
10168+ let zero_loop = self.fresh_label("witness_field_zero_loop");
10169+ let zero_done = self.fresh_label("witness_field_zero_done");
10170+ self.emit_label(&zero_loop);
10171+ self.emit("sltu t5, t4, t3");
10172+ self.emit(format!("beqz t5, {}", zero_done));
10173+ self.emit("add t5, t2, t4");
10174+ self.emit("sb zero, 0(t5)");
10175+ self.emit("addi t4, t4, 1");
10176+ self.emit(format!("j {}", zero_loop));
10177+ self.emit_label(&zero_done);
10178+ self.emit(format!("j {}", done));
10179+
10180+ self.emit_label(&malformed);
10181+ self.emit(format!("li a0, {}", CellScriptRuntimeError::WitnessMalformed.code()));
10182+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
10183+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
10184+ self.emit("ret");
10185+
10186+ self.emit_label(&truncated);
10187+ self.emit(format!("li a0, {}", CellScriptRuntimeError::WitnessFieldTruncated.code()));
10188+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
10189+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
10190+ self.emit("ret");
10191+
10192+ self.emit_label(&invalid);
10193+ self.emit(format!("li a0, {}", CellScriptRuntimeError::CkbSourceViewInvalid.code()));
10194+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
10195+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
10196+ self.emit("ret");
10197+
10198+ self.emit_label(&failed);
10199+ self.emit(format!("li a0, {}", CellScriptRuntimeError::SyscallFailed.code()));
10200+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
10201+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
10202+ self.emit("ret");
10203+
10204+ self.emit_label(&done);
10205+ self.emit("li a0, 0");
10206+ self.emit(format!("ld ra, {}(sp)", RA_OFFSET));
10207+ self.emit(format!("addi sp, sp, {}", FRAME_SIZE));
10208+ self.emit("ret");
10209+ }
10210+
986010211 fn emit_runtime_current_script_hash_helper(&mut self, enabled: bool) {
986110212 self.emit_global("__ckb_current_script_hash");
986210213 self.emit_label("__ckb_current_script_hash");
0 commit comments