EIP-7923 선형 페이지 기반 메모리 과금 (Linear, Page-Based Memory Costing)
Hegota 포함 제외 (DFI) · EVM·실행 · 원문 상태 Draft · 2025-03-27 작성
EVM 메모리 비용을 지금의 2차식에서 페이지(4096바이트) 단위 선형 과금으로 바꿉니다. 처음 건드리는 페이지마다 100가스를 받고, 트랜잭션 전체에 64MB 하드 상한을 둡니다.
왜 필요한가
EVM은 메모리를 쓸수록 비용이 제곱에 가깝게 불어나는 2차식 모델을 씁니다. 처음에는 서비스 거부(DoS) 공격을 막으려는 장치였지만 지금 보면 낡았습니다. 가스 한도 3,000만에서도 한 번의 메시지 호출이 쓸 수 있는 메모리는 3MB에 그치고 그마저 가스를 전부 태워야 합니다. 2차항이 724바이트부터 붙기 때문에 1980년대 초 PC 수준인 64KB를 쓰는 데만 14,336가스가 듭니다. 트랜잭션이 최대 얼마나 메모리를 쓸 수 있는지 알려면 콜 스택 한도와 EIP-150의 63/64 규칙까지 넣은 최적화 문제를 풀어야 해서 추론도 어렵습니다. 또 Vyper나 Solidity 같은 언어는 힙은 아래에서 위로, 콜 스택은 위에서 아래로 키우는 현대적 가상 메모리 구조를 쓸 수 없어 메모리 모델에 비효율이 생깁니다. 이 EIP는 오늘날 하드웨어처럼 메모리를 페이지 단위로 필요할 때만 할당하고 그 값을 선형으로 받는 모델을 제안합니다.
무엇이 바뀌나
지금
- 메모리 비용에 2차항이 붙어 724바이트부터 가파르게 비싸지고 64KB를 쓰는 데 14,336가스가 듭니다
- 가스 한도 3,000만에서도 한 호출이 쓸 수 있는 메모리는 3MB 수준입니다
- 트랜잭션의 최대 메모리 사용량이 콜 스택 상태에 따라 달라져 계산이 어렵습니다
- Vyper와 Solidity가 힙과 콜 스택을 나누는 가상 메모리 기법을 쓸 수 없습니다
EIP-7923 이후
- 처음 건드리는 페이지(4096바이트)마다 100가스를 내는 선형 과금이 되고 첫 페이지는 무료입니다
- 트랜잭션 전체에 64MB(16,384페이지) 하드 상한이 생기고 메모리 주소는 32비트로 제한됩니다
- 메모리 한도가 메시지 콜 스택 상태와 무관해져 얼마나 쓸 수 있는지 바로 알 수 있습니다
- 언어 구현이 가상 주소 공간을 활용해 힙과 콜 스택을 나눠 쓸 수 있습니다
누구에게 영향이 있나
- 컨트랙트·언어 개발자: 메모리를 많이 쓰는 코드가 크게 싸지고 Vyper·Solidity가 힙과 콜 스택을 분리한 메모리 모델을 구현할 수 있게 됩니다.
- 클라이언트 팀: 2차식 확장 비용 로직을 걷어내고 페이지 추적 자료구조를 더해야 합니다. mmap이 있는 시스템에서는 2^32바이트 익명 매핑으로 간단히 구현할 수 있습니다.
- RPC 제공자: 메모리 상한이 가스 한도와 분리되어 메인넷보다 높은 가스 한도로 eth_call을 받더라도 설정 실수로 메모리가 폭주할 위험이 줄어듭니다.
- 사용자: 이전에는 가스 부족으로 실패하던 호출이 새 과금에서는 끝까지 실행될 수 있습니다.
심층 설명
과금 알고리즘 세 가지 변경
첫째, 명령이 건드리는 각 페이지에 대해 이 메시지 호출에서 처음 건드리는 경우 ALLOCATE_PAGE_COST(100가스)를 부과합니다. 페이지 0은 예외로 무료입니다. 둘째, MLOAD·MSTORE 등 모든 메모리 명령의 기본 가스 비용은 3으로 유지됩니다. 셋째, 기존 메모리 확장 비용의 선형 항과 2차 항을 제거합니다. 상수는 ALLOCATE_PAGE_COST = 100, PAGE_SIZE = 4096, MAXIMUM_MEMORY_SIZE = 64 * 1024 * 1024입니다.
32비트 주소 제한과 트랜잭션 전역 상한
메모리 주소는 32비트로 제한됩니다. 주소가 2**32 - 1보다 크면 예외 중단을 일으켜야 합니다. 또 트랜잭션에서 할당한 페이지 수가 MAXIMUM_MEMORY_SIZE // PAGE_SIZE, 즉 16384를 넘으면 역시 예외 중단입니다. 이 상한은 메시지 호출 단위가 아니라 트랜잭션 전역 단위입니다. 호출별 상한(예: 2MB)도 대안으로 검토됐지만 구현 복잡도를 크게 줄이지 못한다는 이유로 트랜잭션 전역으로 정해졌습니다. 2**32바이트라는 주소 상한은 다양한 운영체제와 아키텍처에서 64비트 컴퓨터가 그만큼의 가상 주소 공간을 쉽게 확보할 수 있게 하려는 선택이며 가스 한도나 클라이언트 메모리가 크게 늘면 재검토할 수 있다고 적혀 있습니다.
MSIZE 의미는 그대로
MSIZE의 동작은 바뀌지 않습니다. 메모리 명령이 건드린 최대 바이트를 32 단위로 올림해 반환합니다. 기존 컨트랙트가 MSIZE가 선형으로 자란다고 가정하고 있어 하위 호환을 지키기 위한 선택입니다. 다만 새 가상 주소 방식을 활용하는 컨트랙트는 메모리 할당에 MSIZE를 의존하지 말아야 한다고 원문은 권고합니다.
100가스라는 가격의 벤치마크 근거
2019년형 CPU에서 keccak256을 초당 약 256MB 처리할 수 있다는 점(32바이트당 6가스)에서 1가스당 20ns라는 비율을 잡고 벤치마크를 돌렸습니다. 새 페이지 할당에 1~2us, 2MB 범위 무작위 읽기에 1.8ns, 32MB 범위에 7ns, 4GB 범위에 40ns, mmap 시스템 콜에 230ns가 걸렸습니다. 여기서 페이지 할당 100가스가 나왔습니다. mmap 실행 비용(약 11가스)은 CALL 계열 명령의 기본 비용 100가스가 이미 충분히 지불하고 있고 같은 이유로 페이지 0을 무료로 두었습니다. 덕분에 첫 페이지 안에서는 접근 비용이 기존과 같거나 더 싸져 하위 호환성도 좋아집니다.
두 가지 구현 경로
mmap이 없는 시스템은 가상 주소를 직접 구현합니다. page_id(주소를 12비트 오른쪽 시프트한 정수)를 키로 하는 map[page_id -> char[4096]]을 유지하고 페이지 경계를 걸치는 읽기·쓰기는 두 페이지를 모두 불러 결과를 합칩니다. POSIX mmap(윈도우의 VirtualAlloc)이 있는 시스템은 더 쉽습니다. 2**32바이트 영역을 익명 mmap으로 잡으면 운영체제가 실제로 건드린 페이지만 할당해 주므로, 메모리 읽기·쓰기는 이 버퍼에 대한 단순 접근이 됩니다. 이때 pages 맵은 데이터를 담지 않고 어떤 페이지가 할당됐는지 과금용으로만 추적합니다. 원문에는 py-evm에 대한 약 60줄짜리 패치 형태의 참조 구현이 실려 있습니다.
메모리 상한을 가스 한도와 분리한 이유
클라이언트 구현 쪽에는 DoS 방지를 위해 가스 한도와 별개로 전역 한도를 강제하고 싶다는 요구가 있습니다. 예를 들어 RPC 제공자는 메인넷보다 훨씬 높은 가스 한도로 많은 eth_call을 동시에 처리하도록 설계될 수 있는데, 메모리 한도를 가스 한도에 암묵적으로 묶지 않으면 설정 실수가 생길 통로가 하나 줄어듭니다. 미래에 가스 한도의 제곱근에 비례하는 식으로 하드웨어 발전에 맞춰 한도를 키우는 공식을 만들 수도 있지만 이 EIP의 검토 범위를 좁히기 위해 일단 하드 리밋을 도입했다고 설명합니다.
보안과 하위 호환성 분석
원문은 두 가지를 검토합니다. 첫째, 기존 컨트랙트가 깨지는가. 가스 과금을 빼면 기존 컨트랙트는 가스 부족으로 실패하던 실행이 끝까지 완료될 수 있을 뿐, 하위 호환성은 깨지지 않습니다. 둘째, 클라이언트에 대한 메모리 DoS가 가능해지는가. 지금도 가스 한도 3,000만에서 호출 스택마다 256KB를 할당하는 컨트랙트를 재귀 호출하면 총 54MB를 할당할 수 있어 이 EIP의 64MB 상한과 크게 다르지 않습니다. 트랜잭션 전역 상한 덕분에 앞으로 콜 스택 한도가 바뀌어도 한 트랜잭션이 쓸 수 있는 총 메모리는 변하지 않는다는 불변 조건도 생깁니다.
현재 상태와 포크 논의
이 EIP는 Draft 상태라 명세의 상수와 세부 규칙이 아직 바뀔 수 있습니다. 코어 개발자 콜에서는 2026년 8월 13일 ACDE #243에서 헤고타(Hegota) 포크 후보(PFI)로 접수됐으나, 2026년 9월 10일 ACDE #245에서 제외(DFI)됐습니다. 개선점은 인정됐지만 우선순위가 아니라는 판단이었고 여유가 생기면 재검토할 수 있다는 단서가 붙었습니다.