zksync : zk롤업 EVM 호환성 선두주자
zk롤업인 zksync에 대한 글로 zksync가 어떻게 EVM 호환성 문제를 해결하는지 살펴봅니다. 해당 글은 단순 정보 제공을 위해 작성되었습니다. 투자, 법률, 자문 등 어떤 부분에 대해서도 책임지지 않습니다. 본 글의 내용만을 기준으로 투자에 대한 의사결정을 내리지 마십시오.
Author
nmlyh
Seoul Nat’l Univ. Blockchain Academy Decipher(@decipher-media)
Reviewed By 정재환
목차
- intro
- zksync
-ZKEVM 구조
-EVM호환성
-컴파일러
-경쟁력
-현황 - 마무리
핵심 정리
zk롤업은 기술적 우위에도 불구하고 EVM 호환성 문제로 adoption에 있어 옵티미스틱 롤업에 밀리고 있다. zksync는 이더리움의 명령 코드, ECDSA, API 표준을 지원하고 솔리디티를 ZKEVM 바이트 코드로 바꿔주는 컴파일러를 제공해 이 문제를 해결한다.
1. intro
zk롤업은 영지식 증명을 활용하는 이더리움의 레이어2 솔루션으로 다른 레이어2보다 더 높은 보안과 확장성을 제공할 수 있어 이더리움 커뮤니티가 장기 확장성 솔루션으로 주목하고 있다. 하지만 EVM 호환성 문제로 이더리움 디앱 마이그레이션이 어려워 기술적 우위에도 불구하고 성장 속도가 느리다. EVM 호환성이란 EVM 기능 지원 수준으로 개발자의 입장에서 보면 이더리움 개발자가 기존의 코드를 활용할 수 있는 정도를 의미한다. 호환성이 100%라면 이더리움에서 사용한 코드를 그대로 가져와 사용할 수 있고 0%이면 완전히 새로운 코드를 작성해야 한다. EVM 호환성이 높아야 이더리움 개발자들이 쉽게 디앱을 마이그레이션 할 수 있고 그만큼 생태계가 빠르게 성장할 수 있기 때문에 EVM 호환성은 zk롤업의 핵심 이슈라고 할 수 있다.
수많은 프로젝트가 도전하고 있음에도 높은 수준의 EVM 호환성을 제공하는 zk롤업은 아직 없다. zk롤업의 EVM 호환이 어려운 이유는 zk롤업에서 디앱을 개발할 때 이더리움의 스마트 컨트랙트 로직을 사용할 수 없기 때문이다. 예를 들어, 유니스왑에서 스왑 로직을 수행한다고 해보자. 이더리움에서 사용자가 스왑 트랜잭션을 보내면 EVM에서 명령어를 통해 풀 유동성, 유저의 잔액 등 많은 조건을 확인하고 상태를 변환한다. 영지식 증명을 활용해 이런 동작을 구현할 때는 해당 스왑의 유효성을 입증하는 증명을 생성해야 한다. 이런 기능은 EVM에 존재하지 않기 때문에 EVM 호환성을 제공하는 것은 쉽지 않다. 이 문제를 해결하기 위해 EVM 로직과 ZK 연산을 동시에 지원하는 ZKEVM이 도입됐다. 높은 수준의 EVM 호환성을 제공하는 ZKEVM이 있으면 아주 작은 수정으로 이더리움의 디앱 코드를 그대로 사용할 수 있어 ZKEVM은 zk롤업 생태계 활성화의 핵심 요소라고 할 수 있다. 수많은 프로젝트가 개발하고 있지만 아직 우리는 EVM 호환성을 제공하는 ZKEVM을 볼 수 없다. 그 이유는 ZKEVM 구현의 기술적 난이도가 매우 높기 때문이다. 다음과 같은 이유가 있다.
- ZKEVM의 동작 방식은 EVM과 다르다. EVM은 저장된 스마트 컨트랙트 바이트코드를 로드해 트랜잭션을 수행하고 상태를 전환하지만 ZKEVM은 상태가 올바르게 전환됐다는 증명을 생성해야 한다. 영지식 증명을 생성하는 회로는 복잡하고 EVM은 가스비, 용량 등 많은 제한 사항이 있기 때문에 이 둘을 결합하는 것은 복잡도 매우 높다.
- EVM에는 특수 명령 코드가 많기 때문에 회로 설계가 쉽지 않다. 가스와 관련된 gaslimit과 같은 명령 코드가 있고 컨트랙트와 관련된 delegatecall, 트랜잭션 수행 중 특정 조건을 만족하지 못했을 시 이전까지의 모든 상태 전환을 되돌리는 revert 등 다른 VM에 없는 특수 명령 코드가 많아 회로 설계가 어렵다.
- EVM은 스택 기반 가상 머신으로 연산에 많은 명령어가 필요해 비효율적이다. 이런 구조는 특히 영지식 증명과 관련된 연산 시 비효율적이다. 때문에 ZKEVM은 상당수가 레지스터 기반 모델로 자체 명령어를 정의하지만 이는 EVM 호환을 어렵게 만든다.
- 이더리움의 많은 부분에서 keccak 해시 함수를 사용한다. keccak는 zk 친화적이지 않은 해시 함수이기 때문에 이를 사용해 검증 연산을 실행하면 막대한 연산 오버헤드가 발생한다. zk 연산에 친화적인 poseidon 해시 함수 보다 약 1000배 더 큰 연산이 필요하다. 그 때문에 연산 효율성, EVM 호환성은 동시에 제공하기 힘들다.
이러한 기술적 장벽에도 불구하고 zksync는 높은 수준의 EVM 호환성을 제공한다. zksync가 어떤 수준의 호환성을 제공하고 그것을 제공할 수 있는 이유가 무엇인지 알아보도록 하자.
2. zksync
2–1. ZKEVM 구조
zk롤업은 ZKEVM에서 생성된 증명을 통해 상태 전환의 유효성을 증명한다. 사용자가 트랜잭션을 보내면 EVM 실행기에서 트랜잭션을 실행하고 트랜잭션과 새로운 상태를 ZKEVM에 입력해 증명을 생성한다. 이런 기능을 수행하기 위해서는 많은 연산이 필요하다. 때문에 효율적으로 동작하는 ZKEVM을 설계하는 것은 zk롤업의 확장성에 직접적인 영향을 줄 수 있어 매우 중요하다. zksync는 효율적인 ZKEVM을 구현하기 위해 TinyRam, Recursive Aggregation, Heterogeneous Mixing을 활용해 개발하고 있다. 각각에 대해 알아보도록 하자.
- TinyRam
zksync는 일반 명령 코드에서 높은 효율성을 확보하기 위해 TinyRam을 사용한다. TinyRam은 RISC 기반 머신으로 명령어 집합이 작은 대신 명령어 당 사이클 수를 줄여 효율성이 높다. 그뿐만 아니라 고급 프로그래밍 언어를 컴파일할 때 짧고 효율적인 어셈블리 코드를 지원한다.
단점은 명령어가 복잡해질수록 필요한 게이트가 급격하게 증가한다는 것이다. 위의 표를 보면 add의 경우 일반적인 머신의 게이트보다 1000배 더 많고 Sub는 약 10배 더 많지만 복잡한 연산이 필요한 해시함수의 경우 1000배 이상 더 많은 게이트가 필요하다. 즉 복잡한 연산에서 효율성이 떨어진다. 때문에 zksync는 일반적인 명령어 실행만 TinyRam에서 실행하고 sha256, keccak와 같이 ZKEVM에서 높은 오버헤드가 발생하는 명령어의 경우 따로 특수 회로를 사용해 구현한다.
- Recursive Aggregation
여러 개의 트랜잭션에 대한 증명을 하나로 합쳐 검증 횟수를 줄이기 위해 Recursive Aggregation를 사용한다. 타원곡선의 특성을 사용하면 두 개의 스나크를 동일한 크기의 스나크 하나로 결합할 수 있다. 이런 성질을 활용해 여러 개의 트랜잭션에 대한 증명을 하나로 결합할 수 있다. 예시를 통해 살펴보자.
트랜잭션 t1,t2,t3,t4에 대응하는 스나크 π1, π2, π3, π4가 있다고 했을 때 π1, π2은 π12로 합칠 수 있고 π3, π4는 π34로 합칠 수 있다. π12, π34 역시 합칠 수 있다. 이러한 과정을 거쳐 만들어진 πblock은 블록의 모든 트랜잭션을 증명할 수 있다. 이는 머클 트리와 유사하다. 머클 트리는 블록 데이터를 각각 해시화하고 이를 다시 묶어 해시화해 최종적으로 생성된 하나의 해시값을 통해 블록의 위변조를 효율적으로 검증할 수 있다. 이렇듯 aggregation을 통해 여러 개를 하나로 합치면 작은 값 하나로 전체 데이터를 검증할 수 있다.
- Heterogeneous Mixing
특정 회로 과부하를 방지하기 위해 Heterogeneous Mixing을 사용한다. 회로는 영지식 증명에 사용되는 프로그램 표현을 의미한다. 예를 들어 hash(x) = y를 증명하려면 회로 형식을 사용하여 해시 함수를 다시 작성해야 한다. 블록이 모든 회로를 골고루 사용해 인코딩될 경우 큰 문제가 없지만 블록에 특정 연산 비중이 큰 경우가 많아 특정 회로만 집중적으로 사용할 가능성이 높다. 예를 들어 해시 함수를 많이 사용하는 트랜잭션이 몰려 있는 블록의 경우 해시 함수 관련 회로만 집중적으로 사용하게 될 것이고 해당 회로가 처리할 수 있는 capacity에는 한계가 있기 때문에 전반적으로 효율성이 떨어질 수 있다. 이런 문제를 해결하기 위해 Recursive Aggregation을 통해 hardwired와 TinyRam의 비중을 조절하는 Heterogeneous Mixing을 사용한다. 예시를 통해 살펴보자.
트랜잭션 t에 대한 증명 πi는 πt, πh, πs로 구성된다고 해보자. πt, πh, πs는 TinyRam의 일반적인 operations, hash함수, storage함수와 관련된 증명 중 하나이다. 각각의 트랜잭션에 대해 t, h, s를 분리하고 recursive aggreation을 통해 같은 영역에 속하는 증명들을 합친다. 그렇게 되면 블록에 포함된 모든 트랜잭션의 t 증명, h 증명, s증명이 생성되고 최종적으로 이것들을 합쳐 πblock을 생성한다. 이렇듯 트랜잭션 증명을 각 영역별로 세분화함으로써 특정 회로가 과부하되는 것을 방지한다.
요약하면 zksync의 ZKEVM은 TinyRam을 통해 일반 명령 코드를 구현하고 sha256, keccak와 같이 연산이 많이 요구되는 명령 코드는 hardwired circuits에 구현한다. 생성된 증명들을 Recursive Aggregation를 통해 하나의 증명으로 합치고 이때 특정 회로의 과부하를 막기 위해 Heterogeneous Mixing을 사용한다. 이런 기술을 바탕으로 설계된 zksync의 ZKEVM은 높은 확장성을 제공한다.
이론과 실제는 다르다. 앞서 살펴본 것처럼 zksync의 ZKEVM은 이론상으로는 높은 확장성을 제공하지만 아직 검증이 필요하다. 위의 차트는 zksync의 일별 트랜잭션 숫자이다. 1일 기준 가장 높았던 시기는 약 10만 개로 일평균 10만 개의 트랜잭션을 처리하는 옵티미즘과 아비트럼에 비해서는 덜 활성화됐다는 것을 알 수 있다. 덜 활성화된 만큼 스트레스 테스트를 경험이 적기 때문에 활성화됐을 때 실제 퍼포먼스가 어느 정도일지 판단할 수 없다. matterlabs의 주장처럼 옵티미스틱 롤업보다 압도적인 확장성을 제공할 수 있을지 지켜봐야 한다.
2–2. EVM 호환성
zkSync는 Solidity, Web3 API, Ethers SDK 및 네이티브 이더리움 시그니처를 그대로 사용할 수 있는 zkevm을 개발하고 있다. 어떤 방식으로 호환성을 제공하는지 알아보도록 하자.
- 이더리움 명령어 99% 지원 : zksync에서 스마트 컨트랙트를 개발할 때는 이더리움과 마찬가지로 루프, 재귀, 벡터, map, revert, exception 등을 사용할 수 있다. 특히 스마트 컨트랙트 내에서 다른 스마트 컨트랙트를 호출이 가능해 이더리움에서처럼 디파이 프로젝트들의 상호작용이 가능하다. 스마트 컨트랙트 간의 호출에 쓰이는 call/delegatecall, 실행을 되돌리는 revert 등 핵심 명령 코드를 중심으로 지원한다. 블록 생성 난이도를 반환하는 difficulty, 블록 보상량을 반환하는 coinbase 등 L1에서는 필요하지만 L2에서는 불필요한 특수 명령 코드, 이더리움에서 제외될 것으로 보이는 selfdestruct와 같은 명령 코드는 제외한다. 이더리움의 핵심 기능이면서 롤업 운영에 필요한 명령 코드를 중심으로 ZKEVM 명령어 세트를 개발한다.
- 메타 마스크 등 이더리움 지갑 서비스 사용 가능 : ECDSA를 지원하기 때문에 메타 마스크와 같은 이더리움 지갑 서비스 그대로 사용할 수 있고 따로 개인 키를 등록할 필요가 없다. ECDSA는 이더리움에서 사용되는 서명 알고리즘으로 메시지의 출처와 무결성을 검증한다. 같은 방식의 서명 알고리즘을 사용하면 메타마스크를 사용할 때 블록체인 네트워크만 추가해도 바로 사용이 가능하다.
- 이더리움 인덱서 및 탐색기 통합 가능 : 이더리움의 json-rpc api 표준을 지원하기 때문에 이더리움 인덱서 및 탐색기 통합이 가능하다. json-rpc api 표준은 이더리움 클라이언트와 상호작용할 때 사용하는 형식이다. 같은 형식을 사용하면 이더리움의 인덱서나 탐색기를 그대로 사용할 수 있을 뿐만 아니라 hardhat과 같은 sdk 툴 통합도 가능하다.
- Keccak256, SHA256 지원 : 앞서 살펴본 것처럼 이더리움에서 사용되는 해시 함수는 ZKEVM에서 사용 시 오버헤드가 크기 때문에 사용이 힘들다. zksync는 이런 함수를 프리 컴파일 방식으로 지원한다. 프리 컴파일은 솔리디티와 같은 고급 언어를 기계어로 바꾸기 전에 수행하는 작업이다. 이렇듯 오버헤드가 크지만 EVM 호환성을 위해 필요한 것들은 프리 컴파일 방식으로 지원함으로써 EVM 호환성을 확보한다.
2–3. 컴파일러
zksync는 LLVM 기반 다중 언어 지원 컴파일러를 개발하고 있다. LLVM은 모듈화된 컴파일러 및 도구 기술 모음으로 유연성과 재사용성이 뛰어나 c/c++ 컴파일러 clang, apple swift, rust 등 산업용 컴파일러 개발에 사용된다. zksync는 이것을 활용해 다중 언어를 지원하는 컴파일러를 개발하고 있다. zksync의 컴파일러는 다음과 같은 순서로 동작한다.
- 컴파일러 프론트엔드 : 솔리디티를 컴파일한 중간 언어인 YUL이 LLVM에서 LLVM IR로 변환된다.
- 컴파일러 백엔드 : LLVM IR을 ZKEVM 바이트코드로 컴파일한다.
LLVM IR이란 컴파일러의 중간언어로 프론트 언어와 바이트코드를 이어주고 컴파일러 최적화 및 기계어 코드 생성이 가능하다. 이런 방식을 사용할 경우 효율적인 방식으로 ZKEVM바이트 코드를 생성할 수 있고 다른 개발 언어를 지원할 때는 컴파일러의 프론트엔드에서 해당 언어를 LLVM IR로 바꿔주는 작업만 하면 된다. LLVM을 활용했을 때의 가장 큰 장점은 오픈된 테스팅 소스가 많아 정확한 테스트가 가능하다는 것이다. 컴파일러의 출력 생성과 최적화 정도를 확인하는 회귀 테스트는 36,000개가 존재하며 이중 20,000개를 활용할 수 있고 통합 테스트 소수는 3,000개가 있다. 이를 통해 테스트를 정확하고 효율적으로 진행할 수 있다.
2–4. 경쟁력
현재 EVM 설계로는 EVM 호환성과 ZK연산 효율성을 모두 확보할 수 있는 ZKEVM을 설계하는 것이 매우 어렵기 때문에 EVM 호환성과 ZK연산 효율성 중 하나를 선택해 집중하는 경우가 많다. polygon의 hermez는 EVM 호환성에, starkware는 ZK연산 효율성에 집중하고 있다. zksync는 이들과 달리 EVM 호환성과 ZK연산 효율성의 밸런스를 맞추는 데 집중하고 있다. 컴파일러 기반 언어 호환 방식을 선택해 높은 효율성을 선택함과 동시에 ZK 연산 시 오버헤드가 작은 명령 코드를 중심으로 호환성을 제공하고 일부 오버헤드가 큰 명령 코드에 대해서는 프리 컴파일을 제공함으로써 효율성과 호환성을 동시에 잡기 위해 노력하고 있다. 이러한 방식이 EVM 호환성, ZK 연산 효율성 각각의 파트에서 뛰어난 프로젝트를 따라잡을 수 있는 방식은 아니지만 균형 잡힌 솔루션을 제공해 줄 수 있어 충분히 합리적인 전략으로 볼 수 있다.
2–5. 현황
현재 zksync는 ZKEVM 분야에서 가장 앞서 나가는 프로젝트이다. 지난 2월 EVM 호환성을 제공하는 zksync2.0 testnet을 시작했고 지속적으로 ZKEVM의 효율성을 높이고 호환성을 확보하기 위한 업데이트를 진행하고 있다. 현재는 개발자가 사용한 만큼 수수료를 지불할 수 있도록 하기 위해 수수료 관련 업데이트를 진행 중이고 가을에는 테스트넷에서 ZK PROVER를 경험할 수 있도록 하기 위한 업데이트가 기다리고 있다. 그리고 드디어 올해 말 메인넷이 출시된다. 이는 높은 수준의 EVM 호환성을 제공하는 zk 롤업 최초의 메인넷 출시로 볼 수 있다. 로드맵대로 잘 갈 수 있을지 개발자 및 사용자 온보딩에 성공할 수 있을지 지켜보자.
3. 마무리
zksync는 TinyRam, Recursive Aggregation, Heterogeneous Mixing을 활용한 효율성이 높은 ZKEVM과 유연성-재사용성이 높은 LLVM 기반 컴파일러를 기반으로 높은 수준의 EVM 호환성을 제공한다. zksync는 현재 zk롤업 생태계의 EVM호환성 부문에서 개발 속도가 가장 빠른 zk롤업이기도 하다. 하지만 아직 많은 개발 과제가 남아있고 검증 단계에 있다. 뿐만 아니라 뒤따라오는 경쟁자들도 쟁쟁하다. 빠른 개발을 통해 생태계를 키워나가지 않는다면 따라잡힐 것이고 뒤쳐질 가능성도 높다. zksync가 zk롤업의 EVM 호환성 부문에서 강자의 위치를 유지할 수 있을지 지켜보는 것은 이더리움의 롤업 생태계를 바라보는 중요한 포인트가 될 것이다.