evm2rv는 EVM 런타임 바이트코드, 즉 solc --bin-runtime의 출력을 받아 Rand 프로그램으로 변환합니다. 변환기는 C 코드를 생성하고, rand-guest build가 이 C 코드를 RV32IM 이미지로 컴파일합니다. 이미지는 Rand의 EVM 인터프리터 게스트인 evm.bin과 동일한 입력 벡터를 받고, 동일한 8개의 출력 워드를 게시합니다. 인터프리터처럼 옵코드를 하나씩 해석하는 대신 컨트랙트를 네이티브로 실행합니다. 이 페이지는 ERC-20을 예로 전체 경로를 보여 줍니다.
개요
| 입력 | EVM 런타임 바이트코드, 16진수 텍스트(.hex) 또는 원시 바이트 |
| 출력 | C shim 크레이트. rand-guest build가 이를 이미지 컨테이너로 만듭니다 |
| 실행 위치 | 오프체인, 개발자의 컴퓨터. 체인은 이미지만 봅니다 |
| 일치해야 하는 항목 | evm.bin의 상태, 8개의 출력 워드, gas_used |
| 저장소 | circuits 저장소의 evm2rv/ |
| 고정된 툴체인 | Rust 1.98.1, clang 23.1.1, cc 크레이트 1.4.6 |
변환기는 생성 코드가 아니라 런타임 코드를 입력으로 받습니다. 증명은 배포된 컨트랙트에 대한 호출 한 번을 대상으로 하므로 생성자는 실행되지 않습니다. 생성자가 기록했을 상태는 대신 스토리지 증인(witness)으로서 사전 상태(pre-state)에 포함됩니다.
변환 단계는 두 가지입니다. 두 단계는 결과와 가스가 같으며, 각각 별개의 프로그램이고 고유한 hc를 가집니다.
| 단계 | 플래그 | 동작 |
|---|---|---|
| 1 | --stage 1 | 모든 옵코드를 메모리 스택 위에서 처리 |
| 2 | --stage 2, 기본값 | 각 블록의 스택 워드를 C 지역 변수에 두고, 상수는 변환 시점에 접음 |
입력 벡터는 인터프리터의 것과 같습니다. 코드, 콜데이터, 호출자와 그 밖의 환경 워드, 가스 한도, 사전 상태 루트, 그리고 호출이 접근하는 슬롯마다 하나씩의 스토리지 증인으로 구성됩니다. 출력은 out[0], 즉 상태(1은 성공, 0은 revert, 2는 예외적 중단)와 out[1..8], 즉 코드 해시, 두 상태 루트, 반환 데이터, 로그에 대한 EVM_OUT 다이제스트입니다.
신뢰 모델
verifier는 두 단계로 변환된 컨트랙트를 검증합니다.
hc == translate(bytecode). 게시된 바이트코드에 대해 동일한--stage와--chain-id로evm2rv를 다시 실행하고, 고정된 툴체인으로 다시 빌드한 뒤hc를 비교합니다. 생성된 크레이트는cc를 정확한 버전으로 고정하며 자체Cargo.lock을 포함합니다.- 코드 가드가
hc를 바이트코드에 결속합니다. 변환된 로직은 이미지에 컴파일되어 들어가지만,CODECOPY와CODESIZE는 입력 벡터에 있는 코드를 읽습니다. 검사가 없으면 하나의hc가 호출자가 고른 코드 바이트로 실행될 수 있습니다. 그래서contract.c에는 원본 바이트코드(길이와 바이트)의 Poseidon2 다이제스트가 들어 있고, shim은 변환된 코드가 실행되기 전에 입력 벡터의 코드를 같은 방식으로 해시합니다. 다른 코드는 거부됩니다. 상태 2,gas_used0, 중단 사유OutOfBounds입니다.
EVM_OUT 다이제스트는 인터프리터의 것과 마찬가지로 코드의 keccak256도 결속합니다. 체인은 원본 바이트코드나 그 해시를 기록하지 않습니다. 다른 사람이 hc를 확인할 수 있게 하려면 바이트코드를 게시하거나, Solidity 소스와 정확한 solc 빌드를 게시하십시오.
코드 가드의 비용은 transfer에서 1,008 사이클입니다. 원본과 한 바이트만 다른 코드는 2단계에서 30,418 사이클 만에 거부됩니다(티어 16).
지원 범위와 트랩이 발생하는 경우
옵코드. Shanghai 집합입니다. --evm-version shanghai로 컴파일하십시오. 더 새로운 대상은 MCOPY와 그 밖의 Cancun 옵코드를 생성하며, 이들은 트랩을 일으킵니다.
환경 옵코드:
| 옵코드 | 변환 방식 |
|---|---|
ADDRESS, CALLER, CALLVALUE, CALLDATASIZE, CODESIZE | 인터프리터가 읽는 방식 그대로 입력 벡터에서 읽음 |
CHAINID | 변환 시점에 내장되는 --chain-id 값이므로 hc가 이를 결속합니다. 코드에 CHAINID가 있으면 필수입니다 |
ORIGIN | CALLER와 같음(호출 한 번, 릴레이어 없음) |
GASPRICE, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, SELFBALANCE, BASEFEE, BLOCKHASH | 인터프리터와 마찬가지로 트랩(상태 2) |
블록 컨텍스트 옵코드 9개는 이를 블록에 결속할 설계가 마련될 때까지 트랩을 일으킵니다. 검증자는 H_IN을 열 수 없으므로 비공개 입력은 블록 사실을 담을 수 없습니다. 이 결정이 내려질 때까지, 시간이나 블록 번호를 읽는 컨트랙트(예: permit 만료 시한)는 트랩을 일으킵니다.
호출. CALL, CALLCODE, DELEGATECALL, STATICCALL은 대상이 주소 1~9이면 프리컴파일을 실행합니다. 그 밖의 대상은 트랩을 일으킵니다. CALL 또는 CALLCODE의 0이 아닌 value는 트랩을 일으킵니다. 잔액 모델이 없기 때문입니다.
프리컴파일. 아홉 개 모두 소프트웨어로 실행됩니다. 증명 하나가 최대 2²⁰ 행까지만 담을 수 있으므로, 일부는 현재 증명하기에 너무 느립니다.
| 프리컴파일 | 벡터 | 사이클 | 2²⁰에 들어가는지 |
|---|---|---|---|
| 1 ecrecover | ValidKey | 14,510,525 | 아니오 |
| 2 sha256 | ”abc” / 200 bytes | 2,791 / 5,289 | 예 |
| 3 ripemd160 | ”abc” | 12,195 | 예 |
| 4 identity | 100 bytes | 4,498 | 예 |
| 5 modexp | nagydani-1-square / -1-pow0x10001 | 122,332 / 874,548 | 예 |
| 5 modexp | eip_example1 (256-bit) | 11,170,118 | 아니오 |
| 5 modexp | nagydani-5-pow0x10001 (1,024-byte) | 55,076,950 | 아니오 |
| 6 bn256 add | chfast1 | 978,262 | 예, 간신히 |
| 7 bn256 mul | chfast1 | 3,060,669 | 아니오 |
| 8 bn256 pairing | 1 pair / 2 pairs | 1,853,656,212 / 2,019,000,469 | 아니오 |
| 9 blake2f | 12 rounds | 14,493 | 예, 약 1,800 라운드까지 |
modexp의 base와 modulus는 각각 최대 1,024바이트로 제한됩니다.
허용되는 차이. 상태, 8개의 워드, gas_used가 일치해야 합니다. 세 가지 차이는 허용됩니다. 중단의 종류는 다를 수 있습니다(정적 가스는 각 블록의 선두에서 차감되므로 OutOfGas와 다른 중단이 서로 바뀔 수 있으며, 둘 다 상태 2에 가스를 모두 소진합니다). 변환은 인터프리터가 트랩으로 처리하는 CHAINID, ORIGIN, 프리컴파일 호출을 구현합니다. 그리고 코드 가드는 원본 이외의 코드를 거부합니다.
수치
코드 가드를 포함한 ERC-20 호출입니다. 사이클은 rand-guest run의 값이며, 괄호 안은 티어입니다.
| 호출 | 인터프리터(evm.bin) | 1단계 | 2단계(기본값) |
|---|---|---|---|
transfer(BOB, 250) | 121,638 (18) | 80,211 (18) | 66,235 (18) |
approve(BOB, 5) | 85,645 (18) | 56,261 (16) | 48,119 (16) |
transferFrom(ALICE, BOB, 100) | 161,434 (18) | 110,074 (18) | 88,824 (18) |
| 프로그램 | 워드 | hc |
|---|---|---|
evm.bin, 인터프리터 | 18,009 | 7e1aea2b…854c08 |
| ERC-20, 1단계 | 15,804 | 9cdb79e7…2fd048c8 |
| ERC-20, 2단계 | 11,686 | a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6 |
2단계는 세 호출을 인터프리터 사이클의 54.5%, 56.2%, 55.0%로 실행합니다. transfer는 티어 18에 머뭅니다. 66,235 사이클에 다이제스트 행 3,155개를 더하면 69,390행이고, 이는 티어 16의 65,535행을 3,855행 초과합니다.
2단계 transfer의 약 60%(약 39,928 사이클)는 두 이미지가 공유하는 하네스, 즉 입력 디코딩, 스토리지 증인 검증, ABI의 keccak256 호출, 출력 다이제스트입니다. 어떤 변환 작업도 이 하한 아래로는 내려가지 못합니다.
워크스루: Solidity에서 일치하는 출력까지, ERC-20
모든 명령은 7ef3220 시점의 circuits 체크아웃 루트에서 실행하며, 툴체인은 Deploy a Rust program의 것을, clang 23.1.1은 Deploy a RISC-V program의 것을 사용합니다. 긴 출력은 …로 표시한 곳에서 잘랐습니다.
0. 도구 빌드
(cd evm2rv && cargo +1.98.1 build --release)
(cd rand-guest && cargo +1.98.1 build --release)
1. Solidity를 런타임 바이트코드로 컴파일
컨트랙트는 커밋된 guests-compiled/evm/contracts/ERC20.sol입니다. OpenZeppelin v4의 스토리지 레이아웃을 가진 독립형 ERC-20이며 pragma solidity 0.8.37을 사용합니다. 반드시 solc 0.8.37을 사용하십시오. 다른 버전은 최적화 결과가 달라 바이트코드가 달라집니다.
| 릴리스 | github.com/ethereum/solidity/releases의 v0.8.37 |
| macOS 에셋 | solc-macos, sha256 a27396e7732aa52e80ff89ad7bd8a2e46fec2a6dcc4ef20cd16e5e0c502d6821 |
| Linux 에셋 | 같은 릴리스의 solc-static-linux |
$ curl -sSL -o solc https://github.com/ethereum/solidity/releases/download/v0.8.37/solc-macos
$ shasum -a 256 solc
a27396e7732aa52e80ff89ad7bd8a2e46fec2a6dcc4ef20cd16e5e0c502d6821 solc
$ chmod +x solc && ./solc --version
solc, the solidity compiler commandline interface
Version: 0.8.37+commit.f401782d.Darwin.appleclang
$ (cd guests-compiled/evm && ../../solc --optimize --optimize-runs 200 --evm-version shanghai \
--bin-runtime contracts/ERC20.sol) | tee solc.out
======= contracts/ERC20.sol:ERC20 =======
Binary of the runtime part:
608060405234801561000f575f5ffd5b5060043610610060575f3560e01c8063095ea7b31461006457806318160ddd1461008c57806323b872dd1461…
$ tail -1 solc.out > evm2rv/target/mytoken.hex
마지막 줄이 런타임 바이트코드이며 1,296바이트입니다. 이는 커밋된 guests-compiled/evm/contracts/erc20.runtime.hex와 바이트 단위로 동일합니다. SOLC=$PWD/solc bash guests-compiled/evm/contracts/build.sh가 이를 확인하고 contracts: erc20.runtime.hex reproduces를 출력합니다.
플래그가 중요합니다.
--bin-runtime: 생성 코드가 아니라 배포된 코드입니다.--evm-version shanghai: 0.8.37의 기본 대상은MCOPY를 생성하며, 이는 트랩을 일으킵니다.--optimize --optimize-runs 200: 배포용 컨트랙트 프로파일입니다.
2. 변환
$ evm2rv/target/release/evm2rv evm2rv/target/mytoken.hex --out evm2rv/target/mytoken --chain-id 12
1296 code bytes: 74 blocks, 798 opcodes, 51 jumpdests (stage 2)
CHAINID is the constant 12
no trapping opcodes present
wrote evm2rv/target/mytoken (crate mytoken): contract.c, Cargo.toml, Cargo.lock, build.rs, src/main.rs, shim.ld
--out은 circuits 체크아웃 내부여야 합니다. 이 옵션이 없으면evm2rv는 코드를 분석만 합니다.- 트랩을 일으키는 옵코드는 pc와 함께 여기에 경고로 나열됩니다.
- 이 세션은
--chain-id 12로 기록했습니다. 이 ERC-20에는CHAINID가 없으므로--chain-id는hc를 바꾸지 않습니다.CHAINID를 읽는 컨트랙트는 이 플래그가 필수이며, 배포할 체인으로 설정하십시오(운영 중인 체인은--chain-id 19).
contract.c는 코드 가드의 다이제스트로 끝납니다.
const uint32_t *evm_code_digest(void) {
static const uint32_t d[8] = {0xf679280fu, 0x86c10e83u, 0x7e8f1f39u, 0xa2377de3u, 0x8a2e282fu, 0xc54cad42u, 0xddfaf009u, 0x7d28a8afu};
return d;
}
3. 이미지 빌드
기본 상한은 4,096 워드이므로 --max-words 65535를 지정합니다.
$ rand-guest/target/release/rand-guest build evm2rv/target/mytoken --max-words 65535
…
11686 words against a cap of 65535 (fits); 0 ecall(s) with a non-static a7
OK
wrote evm2rv/target/mytoken/image.bin and its .sha256 (11686 words, hc a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6, program id f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d)
$ rand-guest/target/release/rand-guest info evm2rv/target/mytoken/image.bin
form: image container
text 11147 words at 0x10000; data 408 words (205 non-zero) at 0x1ae2c; prologue 539 words; program 11686 words from base_pc 0xf794
hc a0feae92a7311c9562495717100eb7aea71270a38ed435d06dc31387e0ea8ff6
program id f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d
11686 words against a cap of 4096: does not fit
이것이 게시된 2단계 hc입니다. 크레이트 이름은 이 값에 영향을 주지 않습니다.
4. 입력 벡터를 만들고 transfer 실행
erc20_vector 예제는 패리티 테스트의 벡터를 입력 워드로 출력합니다. transfer(ALICE가 보유한 1,000 중 250을 BOB에게 전송), approve(ALICE가 BOB에게 5의 사용을 허용), transferFrom(BOB이 허용량 300 범위에서 ALICE의 1,000 중 100을 이동)입니다. 직접 만든 컨트랙트에는 Rust에서 같은 방식으로 EvmCall을 구성하십시오. 예제가 템플릿입니다.
$ (cd evm2rv && cargo +1.98.1 run -q --release --example erc20_vector -- transfer > target/transfer.words)
921 input words
$ rand-guest/target/release/rand-guest run evm2rv/target/mytoken/image.bin --input $(cat evm2rv/target/transfer.words)
out[0] = 1
out[1] = 513227413
out[2] = 3087537901
out[3] = 995619457
out[4] = 4004100029
out[5] = 234390638
out[6] = 3262201797
out[7] = 3842361427
cycles 66235
tier 18
5. 인터프리터와 비교
인터프리터 게스트에 같은 입력을 넣습니다.
$ rand-guest/target/release/rand-guest run guests-compiled/bin/evm.bin --input $(cat evm2rv/target/transfer.words)
out[0] = 1
out[1] = 513227413
out[2] = 3087537901
out[3] = 995619457
out[4] = 4004100029
out[5] = 234390638
out[6] = 3262201797
out[7] = 3842361427
cycles 121638
tier 18
그리고 호스트에서 네이티브로 실행되는 인터프리터, 즉 테스트의 오라클입니다.
$ (cd evm2rv && cargo +1.98.1 run -q --release --example erc20_vector -- transfer --expected)
out[0] = 1
out[1] = 513227413
…
out[7] = 3842361427
interpreter (native): status 1, Return, gas_used 29956
한 번에 실행하는 검증 명령은 다음과 같습니다.
$ diff <(rand-guest/target/release/rand-guest run evm2rv/target/mytoken/image.bin --input $(cat evm2rv/target/transfer.words) | grep '^out') \
<(rand-guest/target/release/rand-guest run guests-compiled/bin/evm.bin --input $(cat evm2rv/target/transfer.words) | grep '^out') \
&& echo "the eight words are identical"
the eight words are identical
4단계와 5단계를 approve와 transferFrom으로 반복하면 두 경우 모두 동일한 워드가 나옵니다. cd evm2rv && cargo +1.98.1 test --release는 이 벡터들과 다섯 개의 벡터를 더해 두 단계 모두에서 실행하며 gas_used도 확인합니다(테스트 70개, 무시된 증명 2개 별도).
| 변환본 (2단계) | 인터프리터 (evm.bin) | |
|---|---|---|
| 출력 워드 8개 | 동일 | 동일 |
| 사이클, transfer | 66,235 | 121,638 |
| 티어 | 18 | 18 |
| 프로그램 워드 | 11,686 | 18,009 |
6. 배포
rand program deploy evm2rv/target/mytoken/image.bin
지갑은 프로그램 id, 워드 수, hc를 출력한 뒤 증명을 만들기 전에 체인의 상한을 확인합니다. 이미지는 11,686워드이므로 제네시스에서 max_program_words를 11,686 이상으로 설정한 체인이 필요합니다. 체인 12는 기본값 4,096으로 실행되었으므로 그곳에서는 배포가 거부되었습니다. v0.4 체인인 체인 13은 max_program_words를 65,535로 설정했고, 이후의 모든 체인이 이 값을 유지합니다. 현재 운영 중인 체인 20(제네시스 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135)도 체인 13의 블록, 호출 봉투(call-envelope), 공개 입력 한도를 그대로 유지하며, 증명 크기 상한은 절반인 4 MiB(4,194,304바이트, 체인 17부터이며 체인 13~16은 8 MiB)로 줄였습니다. 워드 상한이 유일한 관문은 더 이상 아닙니다. 체인 20의 제네시스는 hardening_v6를 설정하며, 이에 따라 티어 14 호출 상한에서 한 번의 호출이 담을 수 있는 것보다 많은 프로그램 워드를 배포하면 무효(ProgramUncallable)입니다. 공개 입력이 없을 때 최대 8,180워드입니다. 이 이미지는 11,686워드이므로 체인 20은 배포를 거부하며, 호출도 거부할 것입니다. 모든 ERC-20 호출은 티어 16 또는 18에서 증명되며, 이는 티어 14 호출 상한을 넘기 때문입니다(7단계). 블록 집계는 체인 20에서 꺼져 있습니다(제네시스에 aggregation 섹션이 없습니다). 부트스트랩 노드는 검증인 운영에 나와 있습니다. 배포 수수료는 0.001 + 0.0001 × 11,686 = 1.1696 RAND이며, 아래 체인 13 배포가 지불한 수수료와 같습니다.
이 배포는 2026-09-19에 체인 13이 운영 체인이던 시기(프로덕션 FRI 프로파일, 제네시스 8123ccac1883a45750e4df6964fb7cd3f0b321798cde4c0ef406a0293939ece3)에 제네시스 지갑 shielded-1로 실행되었습니다. 이후의 모든 체인(14~19)은 체인 13의 프로그램 없이 새 체인으로 만들어졌고 이 이미지는 그중 어느 체인에도 다시 배포되지 않았으므로, 아래의 트랜잭션과 높이는 체인 13의 것이며 더 이상 탐색기에서 조회되지 않습니다. 프로그램 id는 base_pc와 워드만의 해시이므로, 배포를 허용하는 어떤 체인에서든 같은 이미지는 같은 id를 가집니다. 진행 상황 줄을 생략(…)한 지갑의 출력은 다음과 같습니다.
$ rand program deploy <image.bin>
program id: f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d (11686 words, hc 92aefea0951c31a717574962aeb70e10a37012a7d035d48e8713c36df68feae0)
…
proved in 92.2s: tier 14, 1419655 bytes
submitted deploy c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0
…
| 프로그램 id | f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d |
| 이미지 | 11,686워드; rand-guest 표기로 hc a0feae92…, 지갑 표기로 92aefea0… |
| 수수료 번들 (체인 13의 2-in-2-out 번들) | 티어 14, 1,419,655바이트, 92.2초에 증명 |
| 배포 tx (체인 13) | c7a66dc4d9cdb7f9bfc4bde01f618c356e5abad7097e6bf7dc04e2e49c53acd0 |
| 커밋된 높이 (체인 13) | 381 |
체인 14부터 수수료를 지불하는 번들은 모든 트랜잭션이 담는 4-in-4-out 숨김 자산(hidden-asset) 번들 하나이며, 트랜잭션의 바인딩에 대해 증명됩니다. 이 번들은 여전히 티어 14이며, 체인 14와 15의 프로덕션 FRI 프로파일에서 약 100초, 약 1.42 MB입니다. 체인 17부터 번들은 티어 10 인증 증명(분할 인증)도 함께 담으므로, 체인 18과 19에서 트랜잭션의 수수료 번들은 번들 증명 약 1.49 MB와 인증 증명 약 1.37 MB입니다.
7. 호출
transfer 호출은 921워드 벡터를 비공개 입력으로 담으며 이는 모든 체인의 입력 상한 이내(기본 4,295워드, 체인 20에서는 16,071워드)이고, 공개 워드는 없습니다. 이 호출은 체인 20에 제출할 수 없습니다. 프로그램을 그곳에 배포할 수 없고(6단계), transfer는 티어 18에서 증명되며, fullnode v0.5.6(2026-09-25)부터 노드는 검증 키를 만들기 전에 티어 14를 넘는 모든 호출 증명을 CallTierTooHigh로 거부하기 때문입니다. 아래 명령은 로컬에서 증명하며 체인은 그 결과를 거부합니다. 측정값은 오프체인 증명입니다. rand call은 각 워드를 반복되는 --input으로 받습니다.
rand call f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d \
$(for w in $(cat evm2rv/target/transfer.words); do printf -- '--input %s ' "$w"; done)
이제 측정되었습니다. 티어 18 증명은 48 GB 노트북에서 24.7 GB에서 종료되었지만, 128 GB 드롭릿(m-16vcpu-128gb)에서는 변환된 transfer 호출이 증명되었습니다. 최대 RSS 85.0 GB, 3,230.5초(53.8분), 증명 크기 811,600바이트였습니다. 검증은 드롭릿에서 101.4초, 48 GB Apple-silicon 노트북에서 3.66 GB RSS로 50.2초가 걸렸습니다(증명 메모리). gas 섹션이 없는 체인(체인 17까지)의 규칙인 원장의 고정 티어 스케줄에서 티어 18의 호출 수수료는 0.0024 RAND였습니다. 체인 18과 19는 대신 호출을 선언된 가스 한도와 바이트 수로 가격을 매기며, 제네시스 가격 기준으로 가스당 100단위, KiB당 800단위이고, 티어 14를 넘는 호출은 허용하지 않습니다.
2026-09-19 체인 13에서의 호출
approve(BOB, 5) 호출(비공개 워드 649개, 티어 16)은 체인 13이 운영 체인이던 2026-09-19에 프로덕션 FRI 프로파일로 같은 지갑에서 실행되었습니다. 이후의 어떤 체인에서도 호출은 이루어지지 않았으며, 그곳에는 프로그램이 배포되어 있지 않고, 체인 20은 이 티어 16 호출 증명을 거부합니다(CallTierTooHigh). 진행 상황 줄을 생략(…)한 지갑의 출력은 다음과 같습니다.
$ rand call f074c4eb834cf01886a8241b6a2e0caf6e1cee5327fee6cb1a1a37436607280d --input <w0> --input <w1> … # the 649 words of approve(BOB, 5)
…
proved in 388.7s: tier 16, 3412405 bytes, outputs [1, 942495465, 790002515, 1351749335, 1059083501, 2923046783, 2814575942, 696258848]
…
proved in 96.0s: tier 14, 1420423 bytes
submitted call 279e1f62abf3f177ca71b08c633e051c37bc4720118c79ddb00936f036c4bfa7
…
| 호출 증명 | 티어 16, 3,412,405바이트, 388.7초에 증명 |
| 수수료 번들 (체인 13의 2-in-2-out 번들) | 티어 14, 1,420,423바이트, 96.0초에 증명 |
| 봉인된 봉투 | 2,700바이트 |
| 호출 tx (체인 13) | 279e1f62abf3f177ca71b08c633e051c37bc4720118c79ddb00936f036c4bfa7 |
| 앵커 높이 / 커밋 높이 | 높이 802 / 높이 919 |
| 수수료 | 0.00357 RAND |
영수증 h_pub | null |
| 영수증 출력 | 같은 벡터에 대한 인터프리터의 여덟 워드와 동일 |
| 소요 시간, 최대 RSS | 622.8초, 22.9 GB, 48 GB M 시리즈 노트북 기준 |
- 프로덕션 증명은 3,412,405바이트로 체인 12의 2 MiB 증명 상한을 넘습니다. 체인 12는 이 호출을 받아들일 수 없었고, 체인 13의 8 MiB 상한은 받아들였습니다. 체인 14
16은 같은 상한을 유지했고, 체인 1720은 4 MiB(4,194,304바이트)로 설정했으며 이 크기의 증명은 여전히 그 안에 들어갑니다. v0.5.6부터 노드는 크기를 확인하기 전에 티어를 이유로 이 호출을 거부합니다. - 수수료에는 체인 13 스케줄의 바이트 항목이 포함됩니다. 3.41 MB 증명은 2 MiB + 18,432바이트의 무료 허용량을 넘습니다. 체인 18부터 모든 호출은 0바이트부터 KiB당 수수료를 지불합니다.
영수증의 여덟 워드는 4단계의 워드와 같습니다. 체인은 EVM 상태를 보관하지 않습니다. 호출자가 사전 상태 루트와 스토리지 증인(witness)을 제공하며, 사후 상태 루트는 EVM_OUT 다이제스트 안에 들어 있습니다.
한도
- 블록 컨텍스트 없음. 블록 바인딩 설계가 마련되기 전까지 아홉 개의 블록 컨텍스트 opcode는 모두 trap됩니다.
- 값 전송 없음. 0이 아닌 값을 가진
CALL은 trap됩니다. 프리컴파일이 아닌 컨트랙트에 대한 호출도 trap됩니다. - ecrecover, bn256 mul, 페어링, 대형 modexp는 현재 증명할 수 없습니다. 각각 2²⁰행 티어 상한을 초과하므로 코프로세서 테이블이 필요합니다.
- 온체인 EVM 상태 없음. 호출은 한 번의 실행에 대한 하나의 증명이며, 상태는 호출자가 가지고 있어야 합니다.
- chain 20에서는 배포도 ERC-20 호출도 허용되지 않습니다. chain 20의
hardening_v6에서는 티어 14 호출이 담을 수 있는 것보다 많은 워드를 배포하면 유효하지 않습니다(ProgramUncallable, 최대 8,180워드). 이미지는 11,686워드입니다. fullnode v0.5.6부터 호출 증명은 티어 14 이하여야 합니다(CallTierTooHigh).approve는 티어 16에서,transfer와transferFrom은 티어 18에서 증명됩니다. 같은 검사는 호출이 선언할 수 있는 해시 테이블도 제한합니다. Keccak 2¹²행(128회 순열)과 SHA-256 2¹³행(128회 압축)입니다. 그럼에도 이 호출들은 아래와 같이 오프체인에서는 증명됩니다. - 스토리지 인덱스의 깊이는 32비트입니다.
evm-core는keccak256(slot)의 상위 32비트로 컨트랙트 스토리지 리프의 위치를 정하므로, 선택된 슬롯과 충돌하는 슬롯을 찾는 데에는 약 2³²회의 Keccak 해시가 듭니다. 그리고 선택된 슬롯이 0이 아닌 값을 담고 있는 동안에는 그 슬롯이 동결됩니다(위조도, 값의 손실도 없습니다). 이는 현재 휴면 상태입니다.evm.bin은 18,009워드이고 티어 14 상한의 호출은 최대 8,184워드를 담을 수 있으므로, 어느 체인에서도 EVM 런타임은 호출할 수 없습니다. fullnode의docs/confidential.md는 호출 상한과 나란히 이 선행 조건을 기록하고 있습니다(audit v6, ZKV-6, v0.6.7 태그 이후인 2026-09-30부터main에 반영). 체인이 티어 14 호출 상한을 해제하거나 호출 가능한 크기의 EVM 런타임을 허용하기 전에, 스토리지 인덱스를 더 깊게 하고evm.bin을 다시 고정해야 합니다. - 증명 완료. 1단계 transfer 증명(티어 18)은 48 GB 노트북에서 1,016초 후 24.7 GB에서 SIGKILL되었지만, 2단계
transfer호출은 128 GB 드롭릿(m-16vcpu-128gb)에서 증명되었습니다. 최대 RSS는 85.0 GB, 소요 시간은 3,230.5초(53.8분), 증명 크기는 811,600바이트였으며, 검증에는 101.4초(드롭릿), 그리고 3.66 GB RSS에서 50.2초(48 GB 노트북)가 걸렸습니다. 인터프리터의evm.bintransfer 증명도 거의 일치했습니다. 85.5 GB, 3,143.3초(52.4분), 805,108바이트의 증명이며, 두 증명은 동일한 공개 출력 워드를 가지므로 실제 증명자에서의 동등성이 확인됩니다.approve(티어 16, 변환됨)는 훨씬 저렴합니다. 21.7 GB, 786.7초(13.1분), 798,930바이트의 증명입니다. 변환은 호출을 더 작은 티어로 내릴 때에만 증명 비용을 낮춥니다. - 체인은 바이트코드를 기록하지 않습니다. 다른 사람이
hc를 다시 빌드할 수 있도록 공개하십시오.