추상적인
이 제안의 궁극적인 목표는 모든 프로젝트가 자체적인 온체인 검증기 스택 대신 채택할 수 있는 표준 L1 기본 요소를 도입하여 L2 브리지, 그리고 더 나아가 ZK 증명을 검증하는 모든 온체인 애플리케이션의 위험을 대폭 줄이고 단순화하는 것입니다. 이는 두 가지 변경 사항을 통해 달성됩니다.
- 이더리움 개선 제안(EIP)-8025를 일반화하여 합의 계층 증명 검증 인프라가 이더리움 가상 머신(EVM) 실행 증명에 종속되지 않고 프로그램에 독립적이 되도록 합니다.
- 새로운 이더리움 개선 제안(EIP) 는 증명 기반 트랜잭션 유형과 세 가지 오퍼레이션 코드(
PROGRAMHASH,PUBVALUESHASH,PROOFCOUNT)를 통해 스마트 계약에 노출합니다.
이 둘을 통해 모든 프로젝트는 L1의 증명 검증 인프라를 직접 상속받을 수 있으며, zkVM 수정 사항은 프로젝트별 관리 업그레이드가 아닌 클라이언트 릴리스를 통해 배포됩니다.
동기 부여
오늘날 모든 이더리움 롤업은 맞춤형 온체인 증명 검증 인프라를 유지합니다. ZK 롤업은 zkVM 검증기 계약, 어댑터 계약, 다중 증명 디스패처, 그리고 프로그램 화이트리스트 로직을 배포합니다. 옵티미스틱 롤업은 자체 온체인 사기 방지 VM(Arbitrum의 WAVM, Optimism의 Cannon MIPS 머신)과 관련 분쟁 해결 로직을 제공합니다. 두 경우 모두 각 계약은 특정 증명 시스템이나 VM의 버그에 대응하여 독립적으로 유지 관리, 패치 및 업그레이드되며, 각 업그레이드는 맞춤형 멀티시그 또는 분산형 자율 조직(DAO) 에 의해 제한됩니다. 이는 느리고 위험하며 생태계 전반에 걸쳐 중복되는 방식입니다.
이더리움 개선 제안(EIP)-8025는 이더 합의 계층에 zkVM 증명 검증 기능을 도입했지만, 이는 L1 계층 자체의 목적, 즉 상태 비저장 및 하위 선형 검증을 가능하게 하는 실행 페이로드 검증에만 사용됩니다. 롤업(Rollup)에는 여전히 자체적인 온체인 검증자 계약이 필요합니다.
하지만 이더리움 개선 제안(EIP)-8025가 CL에 제공하는 인프라, 즉 ProofEngine , 증명 가십, 검증 로직은 본질적으로 L1에 특화된 것이 아닙니다. 프로그램에 구애받지 않도록 일반화하고 새로운 트랜잭션 유형을 통해 스마트 계약에 노출한다면, EVM 기반이 아닌 롤업을 포함한 모든 롤업이 증명 검증을 CL로 오프로드할 수 있습니다. zkVM 구현에 패치가 필요할 때, 이더리움 클라이언트 팀은 geth나 Nethermind의 버그를 수정하는 방식과 동일하게 하드 포크(Fork) 없이 클라이언트 릴리스를 통해 업데이트된 소프트웨어를 배포합니다. 이는 네이티브 롤업 의 원리와 동일하지만 더 일반화된 형태입니다. 네이티브 롤업이 L1의 실행 환경을 상속받는 것처럼, 네이티브 증명 검증은 모든 롤업이 L1의 증명 검증 인프라를 상속받을 수 있도록 합니다.
이 문서에서는 롤업을 중심으로 제안을 구성했지만, 동일한 기본 요소는 개인정보 보호 시스템, ZK 코프로세서, 신원 확인, ZK ML 등 온체인에서 ZK 증명을 검증하는 모든 계약에 적용됩니다.
오늘날 롤업은 어떻게 증명을 검증하는가
각 zkVM 벤더는 범용 솔리디티 검증기 계약(일반적으로 BN254 기반의 Groth16 또는 Plonk 검사)을 제공합니다. 프로그램 식별자와 공개 값(회로가 커밋하는 모든 입력 및 출력)은 증명과 함께 전달됩니다. SP1의 경우:
interface ISP1Verifier {function verifyProof(bytes32 programVKey, // program hashbytes calldata publicValues, // public values (inputs and/or outputs)bytes calldata proofBytes // the proof) external view;} 용어에 대한 참고 사항입니다. SP1에서는 programVKey "검증 키"라고 부르지만, 이는 zkVM 자체의 회로 검증 키와 중복됩니다. 따라서 이 문서에서는 두 가지를 구분하여 사용합니다.
- 프로그램 해시 (SP1에서는
programVKey, Risc0에서는imageId함): 컴파일된 게스트 프로그램을 식별하는bytes32값입니다. 각 zkVM은 서로 다른 방식으로 컴파일되므로(예: RV32IMA와 RV64IMA),(source, zkVM)간의 쌍으로 존재합니다. ERE는 이를 각 백엔드의zkVMVerifier::ProgramVk연관 타입(SP1VerifyingKey, Risc0의Digest이더리움 클래식(ETC) 포함)으로 표현합니다. - 검증 키 : zkVM 회로의 VK(다항식 커밋먼트, 도메인 파라미터). 온체인 검증기에 상수로 하드코딩되어 있으며, zkVM 버전별로 하나씩 존재하고 모든 프로그램에서 공유됩니다.
예시: Taiko(다중 검증 도구)
Taiko는 롤업에 여러 검증 시스템이 사용될 때 발생하는 복잡성을 보여줍니다. Taiko의 검증 아키텍처는 3개 계층(두 개의 원시 검증기, 두 개의 어댑터, 하나의 디스패처, 하나의 SGX 검증기)에 걸쳐 6개의 컨트랙트로 구성되며, 각 컨트랙트는 사용자 정의 멀티시그를 통해 독립적으로 유지 관리되고 업그레이드됩니다.
1. 원시 zkVM 검증기. Taiko는 SP1 Plonk 검증기( SP1Verifier.sol )와 Risc0 Groth16 검증기( RiscZeroGroth16Verifier.sol )를 모두 배포합니다. 이들은 공급업체에서 제공하는 범용 검증기 계약입니다.
2. Taiko 전용 어댑터. 각 원시 검증기는 Taiko의 IVerifier 인터페이스를 구현하는 어댑터 계약으로 래핑됩니다.
// TaikoSP1Verifier: adapter for SP1contract TaikoSP1Verifier is IVerifier {address public sp1RemoteVerifier; // raw SP1 verifiermapping(bytes32 => bool) public isProgramTrusted; // whitelisted programsfunction verifyProof(Context[] calldata _ctxs, bytes calldata _proof) external view {bytes32 aggregationProgram = bytes32(_proof[:32]);bytes32 blockProvingProgram = bytes32(_proof[32:64]);require(isProgramTrusted[aggregationProgram]);require(isProgramTrusted[blockProvingProgram]);bytes memory publicInputs = buildPublicInputs(_ctxs);ISP1Verifier(sp1RemoteVerifier).verifyProof(aggregationProgram, publicInputs, _proof[64:]);}} 병렬 Risc0Verifier isProgramTrusted 대신 isImageTrusted 를 사용하고 저널 다이제스트에 sha256(buildPublicInputs(...)) 사용하는 것을 제외하고는 동일한 형태를 갖습니다.
3. 다중 검증자 디스패처. ComposeVerifier 계약은 여러 검증자를 조율하고 충분한 수의 검증자가 각 증명을 검증했는지 확인합니다.
contract MainnetVerifier is ComposeVerifier {address public immutable sgxGethVerifier; // SGX verifier (required)address public immutable risc0RethVerifier; // Risc0 optionaddress public immutable sp1RethVerifier; // SP1 optionfunction verifyProof(Context[] calldata _ctxs, bytes calldata _proof) external {SubProof[] memory subProofs = abi.decode(_proof, (SubProof[]));for (uint256 i = 0; i < subProofs.length; ++i) {IVerifier(subProofs[i].verifier).verifyProof(_ctxs, subProofs[i].proof);}require(areVerifiersSufficient(verifiers));}function areVerifiersSufficient(address[] memory _verifiers) internal view override {// Must have exactly 2: sgxGethVerifier + (risc0 or sp1)}}이더리움 개선 제안(EIP)-8025 변경 사항
이더리움 개선 제안(EIP)-8025는 L1 블록 검증을 위한 선택적 실행 증명을 도입합니다. 합의 계층에 제공되는 인프라( ProofEngine , 가십, 검증 로직)는 다음과 같은 특징 때문에 L1 전용입니다. ExecutionProof.public_input new_payload_request_root: Root 전달하고, ProofType 허용되는 (client, zkVM) 빌드의 고정된 소수 집합을 열거하는 uint8 입니다( Lighthouse 구현 참조).
ProofType | 게스트 프로그램 | zkVM 백엔드 |
|---|---|---|
| 0 | 에트렉스 | 위험0 |
| 1 | 에트렉스 | SP1 |
| 2 | 에트렉스 | 지스크 |
| 3 | 레스 | 오픈VM |
| 4 | 레스 | 위험0 |
| 5 | 레스 | SP1 |
| 6 | 레스 | 지스크 |
이 방법은 게스트 프로그램 세트가 작고 사전에 알려져 있는 경우에는 작동하지만, 임의의 롤업 프로그램을 수용할 수는 없습니다.
이 이더리움 개선 제안(EIP) 기존 이더리움 개선 제안(EIP)-8025의 표면( ExecutionProof , ProofType , verify_execution_proof , notify_new_payload , notify_forkchoice_updated , process_execution_proof , request_proofs , ProofAttributes )을 그대로 유지하면서 일반적인 검증 프리미티브를 추가합니다. 이러한 일반화는 zkVMVerifier 트레이트가 프로그램에 독립적이며 특정 게스트 프로그램이 그 위에 구축되는 ERE를 반영합니다. Compiler 와 zkVMVerifier 백엔드가 독립적인 트레이트인 ERE의 설계를 따라, 새로운 Proof 컨테이너는 결합된 ProofType 두 축으로 분리합니다. 하나는 zkVM 백엔드만 식별하는 BackendType: uint8 이고, 다른 하나는 게스트 프로그램을 식별하는 program_hash: Bytes32 입니다(특정 (guest program, zkVM) 쌍에 해당, 용어 설명 참조). 엔진은 backend_type 사용하여 회로 VK를 선택합니다. program_hash 는 회로에 대한 공개 입력이며, 검증 과정에서 public_values 와 함께 확인됩니다.
class ProofPublicInput ( Container ):program_hash: Bytes32public_values: ByteList[MAX_PUBLIC_VALUES_SIZE] class Proof ( Container ):proof_data: ByteList[MAX_PROOF_SIZE]backend_type: BackendTypepublic_input: ProofPublicInput def verify_proof ( self: ProofEngine, proof: Proof ) -> bool : ... 이더리움 개선 제안(EIP)-8025의 verify_execution_proof 코드 공유를 위한 verify_proof 위에 얇은 래퍼를 씌운 형태로 재구현할 수 있으며, 가십 계층에서는 눈에 띄는 변경 사항이 없습니다.
def verify_execution_proof ( self: ProofEngine, ep: ExecutionProof ) -> bool :backend_type, program_hash = self .resolve_proof_type(ep.proof_type)expected_public_values = serialize_stateless_output(StatelessValidationResult(new_payload_request_root=ep.public_input.new_payload_request_root,successful_validation= True ,chain_config= self .chain_config,)) return self .verify_proof(Proof(proof_data=ep.proof_data,backend_type=backend_type,public_input=ProofPublicInput(program_hash=program_hash,public_values=expected_public_values,),)) StatelessValidationResult 에 대한 serialize_stateless_output 의 바이트 수준 레이아웃은 네이티브 롤업에 미치는 영향 에서 확인할 수 있습니다. 네이티브 롤업 컨트랙트는 이를 온체인에서 재구성하기 때문입니다. 블록 유효성은 증명 검증과 분리되어 있으며, 정직한 증명자 가이드는 변경되지 않았습니다. 사이드카를 통해 전달된 증명(증명을 포함하는 트랜잭션, 증명 전파 참조)은 L1 래퍼 없이 verify_proof 직접 거칩니다.
프로그램 해시 안정성 (미해결 문제)
네이티브 증명 검증의 "수정 사항은 클라이언트 릴리스를 통해 배포되며 온체인에는 아무런 변화가 없다"는 속성은 한 가지 중요한 요구 사항에 달려 있습니다. 바로 온체인에 고정된 program_hash 값이 zkVM 패치 전반에 걸쳐 안정적으로 유지되어야 한다는 것입니다. 만약 어떤 패치라도 해시 을 변경하면, 이전 값을 고정해 둔 롤업은 업그레이드하지 않는 한 작동하지 않게 되며, 결국 업그레이드 문제는 온체인 거버넌스 문제로 귀결됩니다.
현재 어떤 zkVM도 이를 직접 제공하지 않습니다. 두 유력 후보 모두 회로 계층 수정뿐만 아니라 일반적인 SDK/종속성/툴체인 변경 과정에서 바뀌는 지문 아티팩트를 식별합니다.
- Risc0의
imageIdSystemState { pc: 0, merkle_root }에 대한 SHA-256 해시값이며,merkle_root는 초기 메모리 이미지의 Poseidon2 머클 루트입니다. 이 초기 메모리 이미지에는 사용자 ELF와 커널 ELF가 모두 포함되어 있습니다( binfmt/src/elf.rs#L435 ). 메모리 이미지는 컴파일된 바이트를 그대로 캡처하므로, 종속성 증가, 툴체인 업데이트 또는 커널 패치는 STF 의미 체계가 변경되지 않더라도imageId변경합니다. - SP1의
programVKey는(preprocessed_commit, pc_start, ...)에 대한 Poseidon2 해시입니다( hypercube/src/verifier/hashable_key.rs#L107 ). Risc0의imageId(컴파일된 바이트의 순수 해시 )와 달리 SP1 vk는 ELF에 대한 회로 설정을 실행하는 과정에서 생성되는 부산물입니다.preprocessed_commit은 AIR의 전처리 커밋이고pc_start링커에서 제공되므로 회로 변경, SDK 업데이트 및 툴체인 변경은 사용자의 게스트 소스가 바이트 단위로 동일하더라도 모두 vk를 이동시킵니다.
둘 중 하나를 온체인 program_hash 로 직접 사용하면 모든 zkVM 릴리스가 롤업에 반영되는 이벤트가 됩니다.
현실적인 접근 방식은 간접 계층을 사용하는 것입니다. 온체인 program_hash 안정적이고 롤업 방식으로 선택된 식별자이며 증명에 대한 공개 입력값입니다. 반면 zkVM 내부 식별자는 클라이언트가 관리하고 매 릴리스마다 자유롭게 변경할 수 있는 비공개 입력값입니다. 증명은 이 두 식별자가 연결되어 있음을 입증해야 하며, 이를 통해 안정적인 program_hash 실제로 실행된 내용을 정확하게 반영함을 보여줘야 합니다. 정확한 메커니즘은 아직 설계 단계에서 논의 중인 사항입니다.
NATIVE_PROGRAM 센티널을 사용하는 네이티브 롤업은 이 문제를 완전히 우회합니다. 센티널은 단순히 "L1에서 현재 허용하는 모든 것"이라고 말하며, 허용되는 세트 자체는 zkVM 릴리스와 함께 업데이트되는 클라이언트 측 아티팩트입니다.
새로운 이더리움 개선 제안(EIP): 증명 기반 거래
거래 형식
TransactionType: PROOF_TX_TYPETransactionPayloadBody:[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,to, value, data, access_list, max_fee_per_blob_gas,blob_versioned_hashes, proofs, public_values_hash,y_parity, r, s]어디:
-
proofs:(program_hash, backend_type)쌍의 목록입니다. 각program_hash는 특정 zkVM 백엔드에 대한 게스트 프로그램을 식별하는bytes32값입니다( 용어 설명 참조 ). 각backend_type은uint8값이며, 동일한 백엔드에서 나온 두 개의 proofs는 보안에 영향을 미치지 않으므로 목록 내에서 고유해야 합니다. 이 목록의 길이가proof_count결정합니다. -
public_values_hash: 프로그램의 공개 출력에 대한bytes32해시 (모든 백엔드가 동일한 명제를 증명하므로 모든 증명에서 공유됩니다).
CL 레벨의 Proof 원시 public_values 바이트를 포함하고, 트랜잭션 본문(및 PUBVALUESHASH opcode)은 해당 바이트의 해시 만 노출합니다. 컨트랙트는 예상되는 바이트를 재구성하고 해시값을 비교합니다. 두 가지 불변 조건이 이 두 가지 관점을 연결합니다(사이드카를 처리하는 모든 노드에서, 멤풀 전파 시, 그리고 블록 조립할 때 빌더에서 다시 한번 확인됩니다).
-
sidecar[i].public_input.program_hash == proofs[i].program_hash및sidecar[i].backend_type == proofs[i].backend_type. -
sha256(sidecar[i].public_input.public_values) == public_values_hash.
이러한 요소들은 EVM에서 볼 수 있는 식별자( proofs[i].program_hash , public_values_hash )를 verify_proof 에 전달된 기본 Proof 객체에 연결합니다. 증명이 빌더에 도달하는 방법과 L1 블록 증명이 이를 어떻게 다루는지에 대해서는 증명 전파를 참조하세요.
오프코드
새로운 연산 코드는 증명을 포함하는 트랜잭션의 필드를 읽고 증명을 포함하지 않는 트랜잭션의 경우 0을 반환합니다.
| Opcode | 입력 | 산출 | 설명 |
|---|---|---|---|
PROGRAMHASH | index | program_hash ( bytes32 ) | i번째 증명에 대한 프로그램 해시 . BLOBHASH 와 같은 인덱싱 방식을 사용하며, index >= PROOFCOUNT() 이면 bytes32(0) 반환합니다. |
PUBVALUESHASH | 없음 | public_values_hash ( bytes32 ) | 프로그램의 공개 출력값의 해시 (모든 증명에서 공유됨) |
PROOFCOUNT | 없음 | proof_count ( uint8 ) | 거래 proofs 목록의 길이 |
사용자 지정 롤업은 PROOFCOUNT() 사용하여 반복하고 각 PROGRAMHASH(i) 자체 화이트리스트와 비교하여 확인합니다.
네이티브 롤업의 경우 PROGRAMHASH(i) i번째 증명이 L1이 현재 자체 이더리움 가상 머신(EVM) 실행 증명에 대해 허용하는 프로그램을 사용하는 경우 잘 알려진 센티널 값(예: bytes32(1) )을 반환합니다. 이러한 방식으로 계약은 특정 zkVM별 해시를 저장하지 않고 PROGRAMHASH(i) == NATIVE_PROGRAM 확인하고 클라이언트 릴리스에 포함된 L1 업그레이드를 자동으로 따릅니다.
다중 검증
proofs 목록을 통해 각 롤업은 자체적인 보안/비용 균형을 선택할 수 있습니다. [(hash, SP1)] 은 단일 증명이고, [(hash_sp1, SP1), (hash_risc0, Risc0)] 은 CL이 트랜잭션을 승인하기 전에 동일한 명제가 두 주체에 의해 독립적으로 증명되어야 합니다. 계약은 PROOFCOUNT() 읽고 자체 최소값을 적용합니다.
이는 계약 수준의 다중 증명 오케스트레이션(예: Taiko의 ComposeVerifier 처럼 SGX와 ZK 검증기가 모두 필요한 방식)을 프로토콜 수준의 메커니즘으로 대체합니다. proofs 서명된 트랜잭션 본문에 포함되므로 변조될 수 없습니다.
증명 전파
증명은 멤풀을 통해 빌더에 도달해야 하지만, 장기간 보관할 필요는 없습니다. 제안된 접근 방식은 임시 사이드카 입니다. 증명은 이더리움 개선 제안(EIP)-4844 블롭 사이드카처럼 트랜잭션과 함께 이동합니다. 멤풀 노드와 빌더는 트랜잭션을 전달하거나 포함하기 전에 각 사이드카 항목을 verify_proof 통해 검증하고( 트랜잭션 형식 의 불변 조건도 확인) 처리합니다. 그런 다음 빌더는 블록 에 포함하기 전에 사이드카를 제거하고 재귀적인 L1 블록 증명에 통합한 후 폐기합니다. 검증자는 트랜잭션 본문( proofs 목록 및 public_values_hash )과 L1 블록 증명만 볼 수 있으며, 원시 증명 바이트는 필요하지 않습니다. 따라서 L1 블록 증명은 블록 내의 모든 증명을 포함하는 트랜잭션을 재귀적으로 검증합니다(양자 보안 이후의 증명은 크기가 매우 커서 L1이 슬롯당 하나의 증명으로 제한될 수 있습니다).
크기. 이더리움 개선 제안(EIP)-8025는 증명당 MAX_PROOF_SIZE = 400 KiB 설정합니다. 사양에서는 증명 len(proofs) 를 제한하지 않지만, 메모리 풀 클라이언트 크기 제한으로 인해 실제 최대치는 2~3개입니다.
기존 롤업에 미치는 영향
아래 표는 각 프로젝트의 온체인 계약에 대한 솔리디티 SLOC(공백 및 주석이 아닌 소스 코드 줄)를 보여주며, "핵심" 롤업 로직과 네이티브 증명 검증에서 더 이상 사용되지 않을 증명 검증 스택으로 구분됩니다.
| 프로젝트 | 증명 시스템 | 코어 SLOC | 은퇴한 SLOC | 은퇴자 비율 |
|---|---|---|---|---|
| 아비트럼(Arbitrum) | 낙관적, WASM VM | 19,034 | 8,181 | 43.0% |
| 베이스 | 낙관적, MIPS VM | 17,426 | 8,907 | 51.1% |
| ZKsync 시대 | 유효성, EraVM | 10,823 | 2,379 | 22.0% |
| 리네아 | 유효성, 직접 이더리움 가상 머신(EVM) | 8,111 | 2,460 | 30.3% |
| 거룻배 | 유효성, VM 없음(사용자 지정 회로) | 5,417 | 1,699 | 31.4% |
| 총 | 60,811 | 23,626 | 38.9% |
이 수치는 대략적인 추정치입니다. 온체인 솔리디티 코드만 포함하며 오프체인 증명기, 시퀀서, 그리고 각 program_hash 뒤에 있는 게스트 프로그램은 제외합니다. 거버넌스 표면(멀티시그, 타임락, 분산형 자율 조직(DAO) 계약, 프록시 관리자), 파트너별 브리지, 그리고 프록시 보일러플레이트는 두 열 모두에서 제외됩니다.
타이코의 6개 계약으로 구성된 다중 검증자 스택이 단일 인박스 계약으로 통합됩니다.
contract TaikoInbox {mapping(bytes32 => bool) public isTrustedProgram; // whitelisted per-zkVM program hashesuint256 public minProofCount; // multi-proof threshold (eg 2)function proveBatches(BatchMetadata[] calldata metas,Transition[] calldata trans// _proof parameter removed: verified by the CL) external {// Verify all proofs used trusted programs.require(PROOFCOUNT() >= minProofCount, "insufficient proofs");for (uint256 i = 0; i < PROOFCOUNT(); i++) {require(isTrustedProgram[PROGRAMHASH(i)], "untrusted program");}bytes memory publicInputs = buildPublicInputs(metas, trans);require(PUBVALUESHASH() == sha256(publicInputs), "wrong public values");// Accept the batches....}} isTrustedProgram 화이트리스트 하나로 isProgramTrusted (SP1)와 isImageTrusted (Risc0)를 모두 대체할 수 있으며, minProofCount areVerifiersSufficient 대체합니다.
네이티브 롤업에 미치는 영향
네이티브 롤업의 ZK 사양 에 있는 NativeRollup 계약도 동일한 패턴을 사용합니다. validation_result_root 에 대해 PROOFROOT 하는 대신 PROGRAMHASH , PUBVALUESHASH 및 PROOFCOUNT 확인합니다.
bytes32 constant NATIVE_PROGRAM = bytes32(uint256(1));uint256 public minProofCount;function advance(BlockParams calldata params) external {bytes32 l1Anchor = blockhash(block.number - 1);bytes32 npRoot = computeNewPayloadRequestRoot(blockHash, params.feeRecipient, params.stateRoot,// ... remaining fields ...getVersionedHashes(params.payloadBlobCount),l1Anchor, bytes32(0));// SSZ-encode the StatelessValidationResult container:// new_payload_request_root (32 bytes) || successful_validation (1 byte)// || chain_id (8 bytes, little-endian).// Must match serialize_stateless_output() in execution-specs.bytes memory expectedPublicValues = SSZ.encodeStatelessValidationResult(npRoot, true, chainId);bytes32 expectedPubValuesHash = sha256(expectedPublicValues);require(PROOFCOUNT() >= minProofCount, "insufficient proofs");for (uint256 i = 0; i < PROOFCOUNT(); i++) {require(PROGRAMHASH(i) == NATIVE_PROGRAM, "not a native program");}require(PUBVALUESHASH() == expectedPubValuesHash, "wrong public values");blockHash = params.blockHash;stateRoot = params.stateRoot;blockNumber = blockNumber + 1;stateRootHistory[blockNumber] = params.stateRoot;} 네이티브 롤업은 단순히 programHash L1 자체에서 허용하는 것과 일치하는 롤업입니다. L1 업그레이드(예: verify_stateless_new_payload 변경하는 포크(Fork) )는 자동으로 전파됩니다. 사용자 지정 VM을 사용하는 롤업은 programHash 만 다를 뿐 동일한 패턴을 사용합니다.



