~/blockchain-dev.md
BLOCKCHAIN_DEV

MODEXP 가스가 오르면 왜 암호 검증 컨트랙트부터 봐야 할까: EIP-7883로 보는 precompile repricing

10분 읽기·2026.07.08·출처 5·00
오늘의 질문

precompile은 싸고 안전한 내장 함수라고만 생각해도 될까, 아니면 클라이언트 자원 비용이 바뀌면 내 컨트랙트의 gas budget도 다시 봐야 할까?

blockchain-dev.md
MODEXP Repricing

MODEXP 가스가 오르면 왜 암호 검증 컨트랙트부터 봐야 할까: EIP-7883로 보는 precompile repricing

1. 왜 지금 봐야 하나

Ethereum Fusaka는 PeerDAS나 blob 확장처럼 눈에 잘 보이는 변화만 있는 업그레이드가 아니다. 실행 계층에서는 “한 블록을 더 크게 처리하되, 노드가 감당하기 어려운 최악 케이스는 줄이자”는 안전장치들이 같이 들어간다. 그중 하나가 MODEXP precompile 재가격화다.

MODEXP는 modular exponentiation, 즉 base^exponent mod modulus 계산을 해주는 precompile이다. 주소는 0x0000000000000000000000000000000000000005다. RSA 검증, 일부 cryptographic protocol, 큰 정수 연산, 증명 검증 보조 로직에서 등장한다. Solidity 코드에서는 그냥 외부 호출처럼 보일 수 있지만, 실제 비용은 execution client가 큰 정수 계산을 얼마나 빠르고 안정적으로 처리하느냐에 묶여 있다.

EIP-7883은 이 precompile의 gas cost가 특정 입력에서 실제 자원 사용량보다 낮게 책정되어 있다고 보고, 가격 공식을 바꾼다. 최소 비용은 200에서 500 gas로 오르고, 일반 비용은 기존 공식의 / 3이 제거되며 사실상 3배가 된다. 32바이트를 넘는 exponent의 추가 비용도 더 커진다.

오늘의 핵심은 가격 인상 자체가 아니다.

precompile도 프로토콜의 “공짜 라이브러리”가 아니라, 클라이언트 CPU·메모리·테스트 가능성에 맞춰 계속 재가격화되는 상태 전이 경계다.

2. 핵심 개념

블록체인에서 gas는 단순 수수료 단위가 아니다. 더 정확히 말하면 상태 전이를 검증하는 노드 자원 사용량의 근사치다. 어떤 opcode나 precompile이 실제 계산에 비해 너무 싸면 공격자는 적은 gas로 검증자에게 큰 작업을 시킬 수 있다. 그러면 block gas limit을 올리는 순간 최악 케이스 블록이 네트워크 전파와 검증 시간을 압박한다.

MODEXP의 기존 가격은 EIP-2565에서 정한 공식에 기반한다. EIP-7883은 이 공식에서 세 가지를 바꾼다.

  1. 최소 비용 상향: max(200, ...)max(500, ...)로 바뀐다.
  2. 일반 비용 3배화: 기존에는 multiplication_complexity * iteration_count / 3에 가까웠지만, 새 공식은 / 3을 제거한다.
  3. 큰 입력에 대한 가중치 강화: base/modulus가 32바이트를 넘으면 complexity scaling이 커지고, exponent가 32바이트를 넘는 경우 추가 byte multiplier가 8에서 16으로 오른다.

여기서 개발자가 볼 포인트는 “내 함수 gas가 몇 퍼센트 오르나”만이 아니다. 더 중요한 질문은 이것이다.

  • 내 컨트랙트가 precompile 호출 실패를 어떻게 처리하는가?
  • gasleft()나 고정 gas stipend에 의존하는 부분이 있는가?
  • aggregator, wallet, relayer, bundler가 예전 gas table로 estimate하고 있지 않은가?
  • 하나의 transaction 안에서 여러 번 MODEXP를 호출할 때 누적 비용을 고려하는가?

EIP-7823도 같이 봐야 한다. EIP-7823은 MODEXP 입력 길이에 상한을 둔다. base, exponent, modulus 각각의 길이는 최대 8192비트, 즉 1024바이트로 제한된다. 입력이 이보다 크면 precompile execution은 error를 반환하고 gas를 소모하며 state change는 일어나지 않는다. 이 제한은 “가격을 조금 올리자”가 아니라 “무한한 입력 공간을 테스트 가능한 공간으로 줄이자”는 보안 경계다.

3. 최신 이슈와 연결

EIP-7883 문서는 status가 Final이며 Fusaka/Osaka 실행 계층 변경의 일부로 다뤄진다. execution-spec-tests 쪽에서도 fork:osaka 라벨로 ModExp 테스트가 병합되었다. PR #1579는 EIP-7883과 EIP-7823 관련 테스트 케이스를 추가했고, PR #1729는 최신 spec에 맞춰 테스트를 갱신했으며, PR #1881은 “triple modexp gas price” 변경을 반영했다.

EIP 저장소의 call analysis는 304,301개의 과거 Ethereum mainnet MODEXP 호출을 분석했다. 결과는 명확하다.

  • 분석된 모든 MODEXP call은 새 공식에서 비용이 오른다.
  • 평균 추가 gas는 약 1,784.25 gas다.
  • 평균 비용 배수는 약 2.81x다.
  • 대부분의 입력은 32/32/32바이트 패턴에 몰려 있다.
  • 네트워크 전체 congestion 영향은 평균적으로 작지만, 개별 앱의 gas budget에는 영향을 줄 수 있다.

이 숫자는 “대부분 앱은 괜찮다”와 “아무것도 안 해도 된다”를 구분하게 해준다. 네트워크 평균 영향이 작아도, 특정 앱이 MODEXP를 반복 호출하거나 고정 gas limit에 기대고 있다면 실패 조건이 달라질 수 있다.

Fusaka 문서도 같은 방향을 보여준다. Fusaka는 PeerDAS로 blob data availability를 확장하는 동시에, L1 실행을 안전하게 키우기 위한 여러 제한을 넣는다. transaction gas cap, RLP block size limit, MODEXP input size limit, MODEXP repricing은 모두 같은 메시지를 갖는다.

처리량을 키우려면 평균 케이스보다 최악 케이스를 먼저 통제해야 한다.

4. 개발자 관점 해석

precompile 호출은 “라이브러리 호출”이 아니라 “프로토콜 자원 계약”이다

컨트랙트 개발자는 precompile을 EVM 안에 있는 빠른 함수처럼 생각하기 쉽다. 하지만 precompile은 클라이언트 구현이 직접 처리한다. 같은 상태 전이를 모든 execution client가 같은 결과와 비슷한 시간 안에 처리해야 한다. 그래서 precompile pricing이 틀리면 단순히 사용자가 수수료를 덜 내는 문제가 아니라 consensus client/operator가 감당해야 하는 worst-case workload 문제가 된다.

gas estimate는 fork-aware해야 한다

Fusaka 전후로 같은 calldata가 다른 gas cost를 가질 수 있다. 따라서 SDK, relayer, bundler, wallet backend는 chain config의 fork block을 알고 있어야 한다. “어제 mainnet에서 됐으니 오늘도 된다”가 아니라, eth_estimateGas를 실제 대상 chain/fork 기준으로 호출하고, 내부 gas table을 하드코딩했다면 제거해야 한다.

특히 다음 패턴이 위험하다.

zsh — 생존확인.sh
(bool ok, bytes memory out) = MODEXP.staticcall{gas: fixedGas}(input);
require(ok, "modexp failed");

fixedGas가 과거 가격 공식에 맞춰져 있으면 fork 이후 실패할 수 있다. 더 나쁜 경우는 실패를 false로 받고도 상위 로직이 fallback path를 잘못 타는 것이다. 암호 검증 컨트랙트에서 실패와 “검증 실패”와 “시스템 비용 부족”은 분리해서 기록해야 한다.

account abstraction과 passkey 지갑도 간접 영향을 받을 수 있다

Fusaka에는 P-256 계열 signature verification을 돕는 변화도 있다. 모든 지갑이 MODEXP를 직접 쓰는 것은 아니지만, wallet/account abstraction 생태계는 다양한 cryptographic primitive와 aggregator를 조합한다. 어떤 verifier가 RSA, custom curve, accumulator, proof gadget 때문에 MODEXP에 의존한다면 bundler의 simulation, paymaster budget, user operation gas limit도 영향을 받는다.

핵심은 “EIP-7883 때문에 passkey 지갑이 깨진다”가 아니다. 정확한 교훈은 이것이다.

지갑·relayer·bundler는 cryptographic verifier의 gas model을 프로토콜 fork 단위로 버전 관리해야 한다.

5. 내 프로젝트에 적용할 체크포인트

  1. MODEXP 사용 여부 찾기

    • Solidity 코드에서 0x05, 0x0000000000000000000000000000000000000005, modexp, BigModExp, RSAVerify를 검색한다.
    • 라이브러리 의존성 안의 precompile wrapper도 포함한다.
  2. 고정 gas stipend 제거 또는 재계산

    • staticcall{gas: ...}에 상수가 들어가 있다면 fork 이후 estimate로 다시 계산한다.
    • 반복 호출이면 단일 호출 증가분이 아니라 전체 transaction 증가분을 본다.
  3. 실패 의미 분리

    • precompile call failure, invalid input, cryptographic verification false를 같은 error로 묶지 않는다.
    • EIP-7823 상한 초과 입력은 “검증 실패”가 아니라 “입력 형식/크기 위반”으로 다룬다.
  4. 테스트를 fork별로 돌리기

    • pre-Fusaka와 post-Fusaka chain config에서 같은 test vector의 gas 차이를 기록한다.
    • CI에 gas snapshot을 넣고 MODEXP 관련 함수의 max gas를 따로 표시한다.
  5. relayer/bundler 예산 갱신

    • paymaster가 sponsor하는 최대 gas, bundler simulation timeout, retry policy를 갱신한다.
    • user-facing UI에는 “예상 수수료 상승”보다 “이 작업이 fork 이후에도 실행 가능한가”를 먼저 표시한다.

6. 오늘 10분 액션

오늘 바로 할 수 있는 작은 실험이다.

  1. 저장소에서 modexp, RSA, 0x05, BigNumber, precompile을 검색한다.
  2. 발견된 함수마다 “직접 호출 / 라이브러리 내부 호출 / 테스트 전용”으로 표시한다.
  3. 고정 gas를 넘기는 staticcall이 있으면 숫자를 적는다.
  4. 입력 길이 제한을 체크한다. base/exponent/modulus가 각각 1024바이트를 넘을 수 있는지 확인한다.
  5. gas snapshot에 “post-Fusaka MODEXP repricing 필요” 메모를 남긴다.

작은 팀이라면 오늘은 코드 검색과 메모만 해도 충분하다. 중요한 것은 precompile을 “바뀌지 않는 내장 함수”가 아니라 “fork와 함께 gas contract가 바뀔 수 있는 프로토콜 API”로 보기 시작하는 것이다.

7. 더 볼 자료

중복 회피 메모

최근 blockchain_dev 글은 ERC-1271 fail-closed 서명 검증, Arbitrum BoLD fraud proof finality, EIP-8184 encrypted mempool/MEV, EIP-7702 wallet delegation, bridge parsing/source authentication, ePBS, blob fee reserve, EIP-7825 transaction gas cap과 RLP block size limit을 다뤘다. 이번 글은 block gas cap이나 blob/data availability가 아니라 MODEXP precompile의 gas repricing과 input bound를 통해 “precompile도 클라이언트 자원 비용에 맞춰 재가격화되는 프로토콜 API”라는 관점에 집중해 중복을 피했다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)