EIP-3860 초기화 코드 크기 제한과 과금 (Limit and meter initcode)

최종 확정 · EVM·실행 · 원문 상태 Final · 2021-07-16 작성

컨트랙트를 만들 때 쓰는 초기화 코드(initcode)의 최대 크기를 49152바이트로 제한하고 32바이트 묶음마다 2가스를 추가로 받습니다.

왜 필요한가

이더리움에서 컨트랙트를 새로 만들 때 노드 소프트웨어(클라이언트)는 실행에 앞서 초기화 코드 전체를 훑어 점프 목적지 분석(jumpdest-analysis)을 합니다. 이 작업량은 초기화 코드 길이에 비례해 늘어나는데, 지금은 그 비용을 가스로 받지 않고 프로토콜이 정한 크기 상한도 없습니다. 특히 CREATE·CREATE2 명령으로 컨트랙트 안에서 컨트랙트를 만들 때는 초기화 코드 자체에 붙는 비용이 없어서 변형된 초기화 코드를 값싸게 대량으로 만들어낼 수 있습니다. 실제로 과거에는 악의적인 초기화 코드를 이용한 취약점이 있었고 2017년 geth 1.6.5에서 고쳐졌습니다. 또 상한이 없다는 사실이 다른 EVM 제안들의 설계에 영향을 주고 기능이 미뤄지거나 취소되는 원인이 되기도 했습니다. 이 EIP는 비용이 초기화 코드 길이에 비례하게 만들어 미래의 위험을 줄이고 명시적인 상한을 두어 EVM 구현을 단순하게 만듭니다.

무엇이 바뀌나

지금

EIP-3860 이후

누구에게 영향이 있나

심층 설명

상수와 비용 공식

새 상수는 두 개입니다. INITCODE_WORD_COST는 2, MAX_INITCODE_SIZE는 2 * MAX_CODE_SIZE입니다. MAX_CODE_SIZE는 EIP-170이 정한 24576이므로 초기화 코드 상한은 49152바이트가 됩니다. 비용 함수는 initcode_cost(initcode) = INITCODE_WORD_COST * ceil(len(initcode) / 32)로, 바이트당 환산하면 0.0625가스입니다. EVM은 소수점 가스를 허용하지 않기 때문에 워드(32바이트) 단위로 과금해 근사합니다.

네 가지 규칙

첫째, 생성 트랜잭션의 데이터(초기화 코드)가 MAX_INITCODE_SIZE를 넘으면 트랜잭션 자체가 무효입니다. 인트린식 가스가 모자라는 트랜잭션이 무효인 것과 비슷합니다. 둘째, 생성 트랜잭션의 데이터 비용 공식에 initcode_cost를 더합니다. 이는 인트린식 비용에 포함되므로 이 비용을 못 내는 트랜잭션도 무효입니다. 셋째, CREATE·CREATE2 명령에 넘긴 초기화 코드가 상한을 넘으면 가스 소진처럼 실행이 예외 중단됩니다. 넷째, CREATE·CREATE2에 initcode_cost만큼 가스를 추가로 받으며 이 차감은 컨트랙트 주소 계산과 초기화 코드 실행 전에 이뤄집니다. CREATE2의 해싱 비용보다 먼저거나 같은 시점입니다.

워드당 2가스를 고른 근거

벤치마크의 기준선은 geth 1.10.9의 KECCAK256 해싱 성능으로, 4.0GHz x86_64 CPU에서 70 Mgas/s 가스 한도 목표에 맞춘 것입니다. 측정 결과 32바이트당 비용은 geth가 2.0, evmone/Baseline이 2.9, evmone/Advanced가 13.8, geth의 KECCAK256이 6.0이었고 geth 구현을 기준으로 2가스를 골랐습니다. 워드 단위 계산은 EIP-1014가 정한 CREATE2의 해싱 비용(hashcost)과 같은 방식이어서 구현을 공유할 수 있습니다. 활성화 전에는 CREATE 0, CREATE2 6이던 상수가 활성화 후 CREATE 2, CREATE2 6 + 2가 됩니다.

크기 상한을 둔 이유와 효과

상한이 있으면 최악의 경우를 추정하고 만들어 보기가 쉬워집니다. 탐색해야 할 변수 하나가 크게 줄기 때문입니다. 덕분에 바이트당 비용을 훨씬 낙관적으로 잡을 수 있습니다. 상한이 없다면 알 수 없는 위험까지 대비해 더 비싸게 받아야 하는데, 대부분의 초기화 코드가 제안된 상한을 넘지 않으므로 지나치게 보수적인 비용으로 컨트랙트에 불이익을 줄 필요가 없다고 봤습니다. 2 * MAX_CODE_SIZE라는 상한은 기본적인 경우 런타임 코드에 MAX_CODE_SIZE, 생성자 코드에 또 MAX_CODE_SIZE를 허용합니다. 다만 하나의 생성 트랜잭션에서 여러 컨트랙트를 배포하는 경우에는 실질적인 제약이 될 수 있습니다. 생성 트랜잭션의 초기화 코드 비용(바이트당 0.0625가스)은 데이터 비용(바이트당 4 또는 16가스)에 비해 미미하지만 일관성과 미래 호환성을 위해 명세에 포함했습니다.

위반을 예외 중단으로 처리하는 이유

CREATE·CREATE2에서 상한 위반은 실행의 예외 중단으로 처리합니다. 이렇게 하면 이 위반 검사가 이른 out-of-gas 검사 그룹에 들어갑니다. 이 그룹에는 스택 언더플로, 메모리 확장, 정적 호출 위반, 초기화 코드 해싱 비용, 그리고 이 EIP가 더한 초기화 코드 비용이 있습니다. 이 검사들은 호출 깊이와 잔액 같은 가벼운 검사보다 앞서 수행됩니다. 이 선택은 검사 순서에 일관성을 주고 out-of-gas 검사는 어떤 순서로 해도 되므로 구현 복잡도를 낮춥니다.

하위 호환성과 보안 효과

합의 규칙을 바꾸므로 네트워크 업그레이드(하드포크)가 필요합니다. 이미 배포된 컨트랙트는 영향을 받지 않지만 상한을 넘는 초기화 코드를 담은 트랜잭션은 여전히 블록에 포함될 수 있고 다만 예외 중단으로 끝납니다. 클라이언트 입장에서는 점프 목적지 분석을 노린 공격이 덜 문제가 되어 견고성이 올라갑니다. 원문 작성 시점의 런던(London) 기준 30M 가스 한도에서는 총 약 1.3GB의 초기화 코드에 대한 분석을 유발할 수 있었는데, 이 EIP가 적용되면 그런 공격의 비용이 약 80M 가스 늘어납니다. 이 EIP의 상태는 Final로 확정되었습니다.

EIP 원문 · ← EIPs