EIP-7610 저장소가 남은 주소의 컨트랙트 생성 거부 (Revert creation in case of non-empty storage)
포함 제안 (PFI) · EVM·실행 · 원문 상태 Last Call · 2024-02-02 작성
논스와 코드뿐 아니라 저장소까지 비어 있어야 그 주소에 컨트랙트를 만들 수 있게 합니다. 저장소에 값이 남은 주소에서는 생성이 실패합니다.
왜 필요한가
이더리움에는 한번 배포된 컨트랙트는 바뀌지 않는다는 전제가 있습니다. 이를 지키려고 EIP-684는 대상 주소의 논스(nonce)가 0이고 코드 길이가 0일 때만 새 컨트랙트를 만들 수 있게 했습니다. 그런데 이 두 조건만으로는 부족합니다. EIP-161이 적용되기 전에는 새로 배포된 컨트랙트의 논스가 0으로 남았기 때문에, 생성자(constructor)가 저장소(storage) 값을 써 넣으면 논스 0, 코드 길이 0인데 저장소에만 데이터가 남은 계정이 생길 수 있었습니다. 메인넷에 이런 컨트랙트가 현재 28개 있습니다. 이런 주소는 EIP-684 기준으로는 다시 배포가 가능한 상태라서 처리 방식이 불분명했고 일부 클라이언트는 오로지 테스트를 통과하려고 계정 리셋을 구현하는 등 구현이 갈렸습니다. 이 EIP는 저장소가 비어 있지 않으면 생성 자체를 실패하게 해서 이 구멍을 막습니다.
무엇이 바뀌나
지금
- 논스가 0이고 코드가 없으면 저장소에 값이 남아 있어도 그 주소에 컨트랙트를 만들 수 있습니다
- 저장소가 남은 주소에 배포할 때의 동작이 불분명해 클라이언트마다 구현이 달랐습니다
- 메인넷에 논스 0, 코드 0인데 저장소만 남은 컨트랙트가 28개 있습니다
EIP-7610 이후
- 논스, 코드에 더해 저장소까지 비어 있어야 컨트랙트를 만들 수 있습니다
- 저장소가 남은 주소에서는 생성이 잘못된 명령어를 만난 것처럼 실패합니다
- 이 규칙은 과거의 모든 블록에 소급 적용됩니다
누구에게 영향이 있나
- 클라이언트 팀: 생성 조건 검사에 빈 저장소 확인이 추가되고 테스트 통과만을 위해 넣었던 계정 리셋 구현을 유지할 필요가 없어집니다.
- 컨트랙트 개발자: 배포된 코드가 바뀌지 않는다는 불변성이 명시적인 규칙으로 강제됩니다.
- 일반 사용자: 저장소만 남은 특수한 28개 주소에 배포하는 경우가 아니면 체감 변화가 없습니다.
- EIP-7702 사용자와 지갑: 인가가 적용된 계정은 논스가 증가해 애초에 배포 조건과 충돌하므로 영향이 없습니다.
심층 설명
생성 실패 조건: 논스, 코드, 그리고 저장소
컨트랙트 생성이 생성 트랜잭션, CREATE 명령어, CREATE2 명령어, 그 밖의 어떤 이유로 시도되든, 대상 주소가 다음 중 하나라도 해당하면 생성은 반드시(MUST) 실패합니다. 논스가 0이 아닌 경우, 코드 길이가 0이 아닌 경우, 저장소가 비어 있지 않은 경우입니다. 실패 방식은 초기화 코드(init code)의 첫 바이트가 잘못된 명령어(invalid opcode)인 것처럼 처리합니다. 이 규칙은 기존의 모든 블록에 소급 적용되어야 합니다. 앞의 두 조건은 EIP-684가 이미 정의한 것이고 이 EIP가 더하는 것은 세 번째 조건인 빈 저장소 요구입니다.
EIP-684를 보완하는 이유: 논스 0인데 저장소가 남은 28개 계정
EIP-684는 배포 조건으로 대상 주소의 논스 0, 코드 길이 0 두 가지만 요구합니다. 그런데 EIP-161이 적용되기 전에는 새로 배포된 컨트랙트의 논스가 0으로 남았습니다. 그래서 생성자에서 저장소 슬롯을 설정하면 논스 0, 코드 길이 0이면서 저장소는 비어 있지 않은 컨트랙트를 만드는 것이 가능했습니다. 원문 작성 시점 기준으로 이더리움 메인넷에 이런 컨트랙트가 28개 존재합니다. 이 주소들은 EIP-684 기준으로는 다시 배포가 가능한 상태이므로, 빈 저장소 조건을 추가해 이 틈을 막습니다.
EIP-7702에는 영향 없음
이 EIP는 EIP-7702에 영향을 주지 않습니다. EIP-7702에서는 인가(authorization)가 적용된 뒤 권한자(authority)의 논스가 항상 증가합니다. 컨트랙트 배포는 논스가 0이어야 가능하므로, 인가가 적용된 계정은 애초에 배포 조건과 충돌합니다. 따라서 빈 저장소 조건이 추가되어도 EIP-7702의 동작은 달라지지 않습니다.
테스트 현황과 클라이언트 구현의 불일치
저장소가 비어 있지 않은 대상에 배포하는 시나리오를 다루는 테스트가 ethereum tests 저장소와 execution spec tests에 이미 상당수 존재합니다. 이 테스트들은 과거부터 문제로 여겨졌습니다. Reth와 EELS는 오로지 테스트를 통과하기 위해 일종의 계정 리셋(account reset)을 구현했고 Py-evm은 이 상황이 불가능하다고 선언하고 계정 리셋을 아예 구현하지 않았습니다. 원문은 기존 테스트를 다시 채우는(refill) 것으로 이 EIP에 충분한 테스트 커버리지를 확보할 수 있다고 봅니다.
보안 성격과 적용 방식
이 EIP는 배포된 코드의 불변성을 강제하는 보안 업그레이드입니다. 실행 계층(execution layer)의 변경이므로 하드포크가 필요합니다. 다만 명세의 생성 실패 규칙 자체는 특정 블록부터가 아니라 기존의 모든 블록에 소급 적용되도록 요구합니다. 저장소가 남은 주소에 실제로 배포가 일어난 적이 없다면 과거 블록의 실행 결과는 달라지지 않고 규칙의 정의만 일관되게 정리되는 셈입니다.
진행 상황: 글램스테르담에서 제거 후 완전 취소
원문 상태는 Last Call(마감 2024-11-20)입니다. 포크 일정에서는 다음 결정이 있었습니다. 08.17 ACDT #92에서 EIP-7610을 글램스테르담(Glamsterdam)에서 제거하기로 합의했습니다. Geth와 Reth가 이미 해당 테스트를 건너뛰고 있어 동작 변화가 없다는 판단입니다. 08.20 ACDC #185에서는 연기가 아니라 완전히 취소했습니다. 헤고타(Hegota)의 EIP-8253이 근본 원인을 없애 이 규칙 자체가 필요 없어지기 때문입니다. 09.10 ACDE #245에서는 EIP-8253을 글램스테르담에 뒤늦게 추가하지 않고 원래 예정대로 헤고타에서 다루기로 했습니다. 09.14 ACDT #96에서는 레거시 테스트 PR #18을 병합해 옐로페이퍼와 EIP-684의 충돌 처리 방식으로 되돌렸고 블록 접근 목록 BAL(Block Access List)의 충돌 처리는 EIP-8253이 결론 날 때까지 정의하지 않고 남겨 두기로 했습니다.