Skip to content

fix(arc): charge PQ precompile gas before copying its byte arguments - #256

Merged
littlehand26 merged 1 commit into
mainfrom
fix/arc-pq-reserve-gas
Oct 8, 2026
Merged

littlehand26 merged 1 commit into
mainfrom
fix/arc-pq-reserve-gas

Conversation

@littlehand26

Copy link
Copy Markdown
Contributor

问题

上游 arc-node v0.8.1(circlefin/arc-node#486,相对 v0.8.0 只有这一处改动)修复了 PQ 预编译(0x1800…0004,SLH-DSA-SHA2-128s 验签)的资源消耗问题:原实现先用 abi_decode_raw_validate 把 vk、message、sig 三个 bytes 参数拷贝出来,再扣 base gas 和 message gas。leafage 的移植版 arc/precompile/pq.rs 是同样的顺序。

于是 gas 为 0 或不足的调用也会先分配并拷贝全部参数,然后才因 gas 不足退出。三个 offset 可以指向同一段数据,这时拷贝量是输入长度的 3 倍。PrecompilesMap::run 把 calldata 以借用方式传给预编译(as_bytes_local),EVM 层不拷贝,所以这份工作量只由预编译自己的解码产生。合约可以在一笔调用里用 0 gas 反复调用这个预编译,每次只付约 100 gas 的调用开销,eth_call / simulateTransactions / estimateGas 都能触发。

修改

按上游的做法拆开解码:

  1. abi::decode_sequence + type_check:只读 offset 和长度并做边界检查,参数仍借用输入,不拷贝;
  2. 扣 base gas 和 message gas(message 长度从 token 读取);
  3. detokenize:gas 已付,再拷贝参数。

返回结果不变:

  • alloy-sol-types 1.7.3 的 abi_decode_raw_validate 就是 decode_sequence_with_config(validate=true) → type_check → detokenize;validate 不开 strict 时,解码过程和默认配置一样,所以第 1 步的成功/失败与原来完全相同。detokenize 不会失败,挪到扣 gas 之后只改变拷贝的时机。
  • 上游 v0.8.1 用 1.5.7,与 1.7.3 在非 strict 模式下解码 bytes 的差别只有 1.7.3 新增的 1 GiB 总长度上限;在 EVM 里构造这么大的输入需要的内存 gas 远超任何 gas 上限,碰不到。这一点改前改后一致。

验证

  • 新增 pq_aliased_input_reverts_or_runs_out_of_gas_at_each_charge:三个 offset 指向同一段数据(长度 0 / 32 / 4096),在 gas 为 0、base gas − 1、全额 − 1 时都是 PrecompileOOG;全额时 revert,gas 等于 base + message gas,revert 信息分别为 vk 长度错误或 sig 长度错误。该测试在修复前后都通过,用来固定结果不变。
  • 新增 pq_zero_gas_call_does_not_copy_byte_arguments:1 MiB 数据被三个 offset 共用,gas=0 调用期间本线程分配量须小于 1 MiB。为此在 chains crate 的测试二进制里加了一个只按当前线程计数的 global allocator(所有操作原样转给 System),不受并行测试影响。
  • 修复前(只把 pq.rs 换回 main 版本,测试保留):pq_zero_gas_call_does_not_copy_byte_arguments 失败,gas=0 的一次调用分配 3,151,864 字节(3 × 1 MiB 参数拷贝,加约 6 KB EVM 开销);结果固定测试和原有两个 PQ 测试通过。
  • 修复后:cargo test --locked -p leafage-evm-chains -p leafage-evm-rpc 全部通过:chains lib 575 通过 / 4 ignored,arc_db_error 2/2,b20_gas 6/6,rpc lib 93/93,rpc 集成测试全部通过。
  • 改动文件 rustfmt --check、git diff --check 通过。

不在本 PR

Writer(Chaintable/arc-node v0.8.0-ct.4)的 crates/pq-precompile/src/lib.rs 也是修复前的顺序,需要单独合入上游 v0.8.1。

Port circlefin/arc-node#486 (arc-node v0.8.1). The PQ precompile decoded
and copied vk, message and sig before charging base and message gas, so a
call with zero gas still allocated and copied every argument; aliasing the
three offsets to one array tripled that work.

Tokenize and type-check first (same checks as abi_decode_raw_validate,
arguments stay borrowed), charge gas, then detokenize. Results are
unchanged: detokenize cannot fail, so only the time of the copy moves.
@littlehand26
littlehand26 merged commit 4a10ee9 into main Oct 8, 2026
3 checks passed
@littlehand26
littlehand26 deleted the fix/arc-pq-reserve-gas branch October 8, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant