EIP-8219 체크드 산술 옵코드 (Checked Arithmetic Opcodes)

Hegota 포함 제외 (DFI) · EVM·실행 · 원문 상태 Draft · 2026-04-08 작성

덧셈·뺄셈·곱셈·나눗셈에서 오버플로나 0으로 나누기가 생기면 자동으로 실행을 되돌리는 새 EVM 명령어 4개(SAFEADD, SAFESUB, SAFEMUL, SAFEDIV)를 추가합니다.

왜 필요한가

솔리디티는 0.8.0 버전부터, 바이퍼는 처음부터 산술 연산의 오버플로(계산 결과가 표현 범위를 넘는 것)를 기본으로 검사합니다. 그런데 EVM 자체에는 이 검사를 해 주는 명령어가 없어서 컴파일러가 연산 하나마다 여러 개의 명령어로 된 검사 코드를 끼워 넣습니다. 그 결과 원래 3가스면 되는 덧셈이 솔리디티에서는 약 79가스가 들어, 안전을 위해 25배 비용을 내는 셈이 됩니다. 비용이 부담스러우니 개발자는 unchecked 블록이나 unsafe_add 같은 우회 수단으로 검사를 끄고 싶은 유혹을 받고 안전이 증명되지 않은 곳에서도 검사를 끈 코드가 늘어 감사하기 어려워집니다. 실제로 검사 없는 뺄셈이 악용된 공격 사례도 있습니다. 이 제안은 검사가 내장된 명령어를 EVM에 직접 넣어 안전한 덧셈 비용을 5가스로 낮추고 검사를 우회할 이유 자체를 없애려 합니다.

무엇이 바뀌나

지금

EIP-8219 이후

누구에게 영향이 있나

심층 설명

옵코드 배치와 가스 산정

새 옵코드는 0x0c SAFEADD(5가스), 0x0d SAFESUB(5가스), 0x0e SAFEMUL(7가스), 0x0f SAFEDIV(7가스)입니다. 기존 산술 그룹(0x01 ADD부터 0x0b SIGNEXTEND) 바로 뒤의 현재 미배정 슬롯 4개를 쓰며 이 연속 배치 덕분에 점프 테이블이나 옵코드 범위 검사를 쓰는 인터프리터 구현이 단순해집니다. 가격은 기반 연산 비용에 검사 비용 2가스를 더한 값입니다. SAFEADD·SAFESUB는 Gverylow 그룹 3가스에 2가스, SAFEMUL·SAFEDIV는 Glow 그룹 5가스에 2가스를 더했습니다. 적용 시점 상수 FORK_BLOCK은 아직 TBD입니다.

오류 시 동작: 빈 returndata로 revert

오버플로, 언더플로, 0으로 나누기를 감지하면 현재 콜 프레임을 빈 returndata로 되돌려야 합니다(MUST). REVERT(0, 0) 실행과 의미가 같습니다. 남은 가스를 전부 소모하는 exceptional halt가 아니라 revert를 고른 이유는, 체크드 산술이 슬리피지 검사처럼 revert가 정상적이고 예상된 결과인 곳에 흔히 쓰이기 때문입니다. 빈 returndata는 컴파일러 중립적인 선택으로, Vyper의 현행 오버플로 revert 구현(REVERT(0,0))과는 일치하지만 Solidity가 쓰는 Panic(0x11) 인코딩은 가스·바이트코드 절감과 맞바꿔 포기합니다.

네 연산의 경계 조건

SAFEADD는 a + b > 2^256 - 1이면 revert합니다. SAFESUB는 b > a(언더플로)면 revert하며 스택 순서는 기존 SUB와 같이 a가 위, 결과는 a - b입니다. SAFEMUL은 a == 0이면 b와 무관하게 0을 내고 revert하지 않으며 그 외에는 결과를 a로 나눠 b와 다르면(오버플로) revert합니다. SAFEDIV는 스택 순서가 기존 DIV와 같고(피제수 a가 위), 제수 b == 0이면 revert합니다. 부호 없는 정수 나눗셈은 오버플로가 불가능해 따로 검사하지 않습니다. 테스트 케이스 기준으로 0을 0으로 나누는 경우(SAFEDIV의 a=0, b=0)도 revert합니다.

선행 제안(EIP-1051, EIP-6888) 플래그 방식과의 차이

EIP-1051은 기존 산술 옵코드가 ovf·sovf 플래그를 세우고 OFV·SOVF 옵코드로 읽는 방식, EIP-6888은 carry·overflow 플래그와 JUMPC·JUMPO 조건부 점프를 쓰는 방식입니다. 두 방식 모두 연산 뒤에 플래그 읽기와 조건부 점프가 필요해 최소 3개 명령어가 들고 명령어 사이에 남는 암묵적 상태(플래그)가 생겨 정적 분석·형식 검증·컴파일러 최적화가 복잡해집니다. 덧셈 두 번 사이에 플래그를 한 번만 확인하면 앞선 오버플로가 조용히 사라지는 문제도 있습니다. 이 EIP는 각 옵코드가 올바른 결과를 내거나 revert하는 자기완결적 무상태 명령이라 이런 문제가 없고 추가 명령어가 없어 가스 절감 폭도 최대입니다.

벤치마크 수치

Solidity 0.8.33(옵티마이저 200 runs)과 Vyper 0.4.3으로 체크드 덧셈 하나를 분리한 동일 ERC-20 컨트랙트를 측정했습니다. 실행 가스는 덧셈당 Solidity 약 79가스(3 + 76), Vyper 약 41가스(3 + 38)인데 SAFEADD는 5가스로, 각각 93.7%와 87.8% 절감입니다. 바이트코드는 덧셈당 12바이트(Solidity)·14바이트(Vyper)가 1바이트로, 배포 가스는 +2,352·+2,604가 약 200으로 줄어듭니다. 벤치마크 컨트랙트에는 transfer와 transferFrom에 각각 하나씩 체크드 덧셈 두 개가 있어 컨트랙트 전체 합계는 이 수치의 두 배이며 Solidity의 오버플로 검사 헬퍼는 컨트랙트당 한 번만 내보내져 모든 체크드 덧셈이 공유합니다.

부호 없는 256비트만 다루는 이유와 옵코드 4개의 정당화

이 EIP는 부호 없는(unsigned) 연산만 정의합니다. 부호 있는(two's complement) 오버플로는 INT256_MIN / -1 같은 다른 경계 조건이 있어, 부호 있는 변형(SSAFEADD, SSAFESUB, SSAFEMUL, SSAFEDIV)은 별도의 동반 EIP로 미뤘습니다. Solidity와 Vyper의 지배적 숫자 타입이 uint256이라 부호 없는 연산이 대부분을 차지하고 같은 이유로 더 작은 데이터 타입도 다루지 않습니다. 옵코드 슬롯 4개를 쓰는 비용은 크지만 Devcon SEA 발표('EVM Charts 2024') 전의 분석에서 기존 사칙연산 옵코드 4개가 사용 빈도 상위 35위 안에 들었고 체크드 산술이 두 언어의 기본값이라는 점이 새 컨트랙트에서의 많은 사용을 뒷받침한다고 봅니다.

하위 호환성

0x0c~0x0f는 현재 미배정 슬롯으로, 지금 실행하면 INVALID처럼 동작해 모든 가스를 소모합니다. 기존 ADD, SUB, MUL, DIV 등은 전혀 바뀌지 않으므로, Solidity의 unchecked 블록이나 Vyper의 unsafe_add()를 쓰는 컨트랙트는 지금과 같은 의미의 원시 옵코드를 계속 씁니다. 컴파일러는 포크 이후 EVM 버전을 타깃으로 지정해야 새 옵코드를 쓰고 이전 EVM 버전으로 컴파일된 컨트랙트는 동일하게 동작합니다.

진행 상태와 콜 결정

원문은 Draft 상태라 명세가 아직 바뀔 수 있습니다. 코어 개발자 콜에서는 08.13 ACDE #243에서 헤고타(Hegota) 포크 후보(PFI)로 접수됐지만 09.10 ACDE #245에서 전 클라이언트 만장일치 저평가(0.0~0.8)를 이유로 EIP-2488, EIP-8182, EIP-7645와 함께 제외(DFI)됐습니다.

EIP 원문 · ← EIPs