CLZ opcode는 왜 작은 수학 함수가 아니라 EVM 비용 모델의 변화일까: EIP-7939로 보는 비트 연산 기본기
count leading zeros 같은 낮은 수준의 비트 연산 하나가 왜 스마트컨트랙트 가스, ZK 증명 비용, bytecode 크기까지 바꿀까?
CLZ opcode는 왜 작은 수학 함수가 아니라 EVM 비용 모델의 변화일까: EIP-7939로 보는 비트 연산 기본기
- 카테고리: blockchain_dev
- 예상 읽기 시간: 10분
- 오늘의 질문:
count leading zeros같은 낮은 수준의 비트 연산 하나가 왜 스마트컨트랙트 가스, ZK 증명 비용, bytecode 크기까지 바꿀까? - 핵심 출처:
- EIP-7939: Count leading zeros (CLZ) opcode - 생성일 2025-04-28, 확인일 2026-07-15
- ethereum/EIPs PR #9701: Add EIP: Count leading zeros (CLZ) opcode - 병합 2025-04-29, 확인일 2026-07-15
- ethereum/EIPs PR #9987: Update EIP-7939: Change gas cost from 3 to 5 - 병합 2025-07-07, 확인일 2026-07-15
- execution-spec-tests: Osaka EIP-7939 CLZ spec.py - main branch, 확인일 2026-07-15
- execution-spec-tests: Osaka EIP-7939 test_count_leading_zeros.py - main branch, 확인일 2026-07-15
1. 왜 지금 봐야 하나
최근 Ethereum Fusaka/Osaka 계열 EIP를 보면 큰 주제만 보이기 쉽다. blob, PeerDAS, gas limit, block size, precompile repricing 같은 것들이다. 그런데 EIP-7939는 훨씬 작아 보인다. 새 opcode 하나, CLZ를 추가한다. CLZ(x)는 256-bit word에서 가장 앞쪽의 0 bit가 몇 개인지 세고, x == 0이면 256을 반환한다.
작아 보여도 개발자에게 중요한 이유가 있다. 스마트컨트랙트에서 비트 연산은 “마이크로 최적화”가 아니라 다음 세 영역의 공통 기반이다.
- 고정소수점 수학:
sqrt,cbrt,lnWad,powWad같은 함수는 숫자의 크기 범위를 빠르게 알아야 한다. - 데이터 구조: bitmap에서 다음 set bit/unset bit를 찾을 때 leading/trailing zero 계산이 핵심이다.
- 증명 비용: zkVM이나 ZK 회로 안에서 동적 shift를 많이 쓰는 코드는 실행 가스보다 proving cost가 더 크게 문제가 될 수 있다.
EIP-7939는 이 공통 연산을 Solidity 라이브러리 트릭에서 EVM 기본 명령어로 내린다. 즉 오늘의 포인트는 “새 opcode가 생겼다”가 아니라, 자주 쓰이는 추상 연산을 VM primitive로 승격할 때 비용 모델과 감사 포인트가 어떻게 바뀌는가다.
2. 핵심 개념
CLZ는 count leading zeros의 약자다. 256-bit EVM word를 왼쪽, 즉 most significant bit 쪽에서부터 보면서 1이 나오기 전까지의 0 개수를 센다.
예를 들어:
0x8000...0000은 맨 앞 bit가 1이므로CLZ = 00x4000...0000은 앞에 0이 하나 있고 그다음 bit가 1이므로CLZ = 10x0000...0001은 마지막 bit만 1이므로CLZ = 2550x0000...0000은 1이 전혀 없으므로 특수 케이스로CLZ = 256
EIP-7939의 specification은 새 opcode를 0x1e로 정의하고, stack에서 값 하나를 pop한 뒤 결과 하나를 push한다고 설명한다. execution-spec-tests의 spec.py도 같은 상수를 둔다.
CLZ = 0x1E
CLZ_GAS_COST = 5
def calculate_clz(value: int) -> int:
if value == 0:
return 256
return 256 - value.bit_length()
개발자가 눈여겨볼 숫자는 gas cost 5다. EIP 본문은 원래 빠른 Solidity 구현이 약 184 gas를 사용한다고 예시를 든다. 반면 opcode는 5 gas다. PR #9987은 CLZ gas cost를 3에서 5로 올렸는데, 이유를 “under-pricing DoS risk를 피하기 위한 보수적 상한”이라고 설명한다. 여기서 중요한 설계 감각은 이것이다.
VM primitive는 너무 비싸면 아무도 안 쓰고, 너무 싸면 DoS 표면이 된다. 그래서 “빠른가?”만큼 “최악 비용이 상수로 안전한가?”가 중요하다.
3. 최신 이슈와 연결
EIP-7939는 상태가 Final이고, EIP 저장소 PR #9701로 2025년 4월 29일 병합되었다. 이후 PR #9987에서 gas cost가 3에서 5로 조정되었다. execution-spec-tests는 tests/osaka/eip7939_count_leading_zeros 아래에 CLZ 상수, reference spec commit, edge case 테스트를 포함한다.
테스트가 흥미로운 이유는 단순 happy path만 보지 않기 때문이다. test_count_leading_zeros.py는 다음 케이스를 생성한다.
- 전체 0: 결과 256
- leading zeros가 0부터 256까지 변하는 값
- single bit만 켜진 값
- 임의의 큰 256-bit 값
이 테스트 목록은 개발자에게 좋은 힌트를 준다. CLZ 같은 연산은 “대충 숫자 크기 세는 함수”로 보면 안 된다. 경계값이 곧 consensus semantics다. 특히 0 -> 256은 일반적인 8-bit/32-bit 감각과 다르게 보일 수 있으므로 컨트랙트 wrapper와 라이브러리에서 명확히 다뤄야 한다.
4. 개발자 관점 해석
4.1 수학 라이브러리: 분기와 lookup table을 줄일 수 있다
고정소수점 수학 라이브러리는 입력 숫자의 magnitude를 빠르게 파악해야 한다. 지금까지는 여러 번의 SHR, 비교, magic constant, lookup table을 조합했다. EIP 본문도 빠른 Solidity 구현이 여러 dynamic bitwise right shift와 큰 constant를 사용한다고 설명한다.
CLZ가 있으면 다음과 같은 코드가 단순해진다.
// 개념 예시: 실제 사용은 fork 지원 여부와 compiler 지원을 확인해야 한다.
uint256 msbIndex = 255 - clz(x); // x != 0일 때
이 변화는 가스만 줄이는 게 아니다. 리뷰어가 “이 magic constant가 맞나?”를 검토하던 일을 “CLZ 결과를 어떤 경계 조건으로 쓰는가?”로 바꾼다.
4.2 bitmap: next bit 탐색 비용이 예측 가능해진다
DEX tick bitmap, NFT/slot allocation bitmap, permission bitmap처럼 bitset을 쓰는 컨트랙트는 다음 set bit를 찾는 로직을 자주 가진다. CLZ는 leading direction 탐색을 자연스럽게 만든다. trailing zero가 필요하면 EIP rationale처럼 가장 낮은 set bit를 x & -x로 isolate한 뒤 CLZ로 위치를 계산할 수 있다.
핵심은 loop를 줄이고 worst-case를 상수화하는 것이다. 블록체인에서는 평균 성능보다 최악 입력에서 gas가 폭발하지 않는지가 더 중요하다.
4.3 ZK/rollup: 실행 가스와 proving cost는 다르다
EIP-7939의 motivation은 ZK proving cost를 직접 언급한다. 빠른 Solidity 구현은 dynamic shr를 여러 번 쓰는데, 특정 zkVM에서는 shift가 multiplication보다 더 비쌀 수 있다고 설명한다. 이 말은 블록체인 개발자가 점점 더 자주 마주치는 구분을 보여준다.
- L1 gas: EVM에서 한 트랜잭션이 내는 비용
- prover cost: 같은 계산을 증명으로 만들 때의 비용
- bytecode size: 배포와 코드 로딩, 감사 난이도에 영향을 주는 비용
CLZ는 이 셋을 동시에 줄일 수 있는 primitive다. 그래서 “opcode 하나”가 rollup prover, on-chain verifier, math-heavy DeFi library의 공통 관심사가 된다.
5. 내 프로젝트에 적용할 체크포인트
-
fork 지원 여부를 분리하라
- 새 opcode는 해당 fork 이후 체인에서만 유효하다.
- L1, L2, local anvil/hardhat, forked network가 같은 opcode table을 쓰는지 확인한다.
-
x == 0정책을 명시하라- CLZ는 0에 대해 256을 반환한다.
msb = 255 - clz(x)같은 식은x == 0에서 underflow/의미 오류가 날 수 있다.- wrapper에서
require(x != 0)또는 별도 branch를 둔다.
-
기존 assembly 최적화를 바로 삭제하지 말라
- compiler가 CLZ intrinsic을 언제 지원하는지 확인해야 한다.
- inline assembly로 opcode를 직접 쓰면 chain compatibility와 테스트 환경이 더 중요해진다.
-
가스 최적화와 감사 가능성을 같이 보라
- magic constant 기반 구현은 빠르지만 리뷰 난이도가 높다.
- CLZ 도입 후에는 “상수 검증”보다 “경계값과 fork guard”가 감사 포인트가 된다.
-
ZK 프로젝트라면 proving benchmark를 따로 재라
- EVM gas가 낮아져도 특정 proving backend에서 효과가 얼마인지는 별도 측정이 필요하다.
- 특히 bitmap, compression, hash-to-field 전처리, PQ signature 관련 코드는 후보가 된다.
6. 오늘 10분 액션
아래처럼 내 코드베이스에서 CLZ 후보를 찾아보자.
# Solidity/Yul에서 비트 크기 계산, shift-heavy 코드 찾기
rg "shr\(|shl\(|bitLength|mostSignificantBit|msb|leading|bitmap|ctz|clz" contracts test
그리고 후보마다 표를 만든다.
| 위치 | 현재 구현 | 0 입력 처리 | loop/shift 수 | CLZ로 바꿀 수 있나 | fork guard 필요 |
|---|---|---|---|---|---|
| Math.sqrt | assembly msb | revert | 여러 SHR | 가능 | 예 |
| TickBitmap.next | mask + scan | skip | branch 많음 | 일부 가능 | 예 |
| Compression.decode | byte scan | 32 byte skip | loop | 가능성 검토 | 예 |
마지막으로 테스트 케이스에 최소한 아래 값을 넣는다.
0
1
2**255
2**255 - 1
2**128
2**128 + 1
2**256 - 1
7. 더 볼 자료
- EIP-7939: Count leading zeros (CLZ) opcode
- ethereum/EIPs PR #9987: gas cost 3 -> 5
- execution-spec-tests Osaka EIP-7939 tests
- Ethereum Yellow Paper / EVM opcode table을 함께 확인하기
중복 회피 메모
로컬 content/generated와 Supabase의 최근 blockchain_dev 글을 확인했다. 최근 글은 EIP-7907 contract code size gas, Solana Alpenglow finality, Taiko bridge origin verification, OP Stack fault proof soundness, ERC-4626 NAV manipulation, P-256/passkey precompile, MODEXP repricing, EIP-7702 delegation, PeerDAS/BPO/blob fee reserve 등을 다뤘다. 이번 글은 DA, bridge, finality, precompile repricing이 아니라 EIP-7939 CLZ opcode를 통해 EVM primitive 하나가 math library, bitmap, bytecode size, ZK proving cost, fork compatibility에 주는 영향을 다뤄 중복을 피했다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)