EIP-3541 0xEF로 시작하는 새 코드 배포 금지 (Reject new contract code starting with the 0xEF byte)
최종 확정 · EVM·실행 · 원문 상태 Final · 2021-03-16 작성
첫 바이트가 0xEF인 컨트랙트 코드를 새로 배포할 수 없게 막습니다. 이미 배포된 코드는 의미가 바뀌지 않고 그대로 유지됩니다.
왜 필요한가
이더리움은 지금까지 컨트랙트 코드를 배포할 때 형식을 검사하지 않고 그대로 저장했습니다. 앞으로 도입하려는 새 코드 형식인 EVM 오브젝트 포맷(EVM Object Format, EOF)은 배포 시점에 코드가 올바른 형식인지 검증하는 것이 핵심입니다. 그런데 이 보장이 성립하려면 검증 없이 이미 배포된 옛 코드가 우연히 새 형식으로 오인되는 일이 없어야 합니다. 그래서 어떤 기존 컨트랙트에도 쓰이지 않은 바이트 값을 형식 표시(매직)로 골라야 하는데, 시간이 지나면서 그런 코드가 새로 배포되면 고를 수 있는 값이 줄어듭니다. 이 제안은 매직의 첫 바이트로 정한 0xEF로 시작하는 코드의 배포를 지금 막아서 확인해야 할 범위가 더 커지지 않게 하고 분석 대상을 이 포크 이전에 존재하던 컨트랙트로 한정합니다.
무엇이 바뀌나
지금
- 첫 바이트가 어떤 값이든 컨트랙트 코드를 배포할 수 있습니다
- 배포된 코드는 아무 검증도 거치지 않고 상태에 저장됩니다
- 새 코드 형식을 도입해도 옛 코드와 구분할 안전한 표시가 없습니다
EIP-3541 이후
- 첫 바이트가 0xEF인 코드는 배포가 실패합니다
- 이미 배포된 0xEF 시작 코드는 그대로 남고 동작도 바뀌지 않습니다
- 0xEF가 새 코드 형식을 구분하는 표시로 예약됩니다
누구에게 영향이 있나
- 일반 사용자: 체감 변화가 없습니다. 0xEF로 시작하는 코드는 지금도 실행할 수 없기 때문입니다.
- 컨트랙트 개발자: 코드처럼 실행하지 않고 데이터 저장용으로 쓰는 컨트랙트라도 첫 바이트가 0xEF면 배포할 수 없습니다.
- 클라이언트 팀: create 트랜잭션과 CREATE, CREATE2 처리에 코드 첫 바이트 검사를 추가해야 합니다.
- EOF 설계자: 상태에 있는 EOF 형식 코드는 전부 검증을 거쳤다고 보장할 수 있는 기반이 생깁니다.
심층 설명
규칙: 세 가지 생성 경로 모두에서 거부
HF_BLOCK 이후 블록부터, 새 컨트랙트 생성 시 계정에 들어갈 code의 첫 바이트가 0xEF면 예외 중단(exceptional abort)으로 실패합니다. 생성 경로는 세 가지 모두 해당합니다. create 트랜잭션, CREATE 명령, CREATE2 명령입니다. 검사 대상은 initcode가 RETURN으로 반환한 code이며, initcode 자체가 아닙니다. 이 예외 중단은 initcode 실행 중 일어나는 다른 예외 중단과 동일하게 동작하므로, CREATE*나 create 트랜잭션에 제공된 가스는 전부 소모됩니다.
기존 코드와 initcode는 영향 없음
0xEF는 현재 정의되지 않은 명령어입니다. 스택에서 아무것도 꺼내거나 넣지 않고 실행되면 예외 중단을 일으킵니다. 따라서 0xEF로 시작하는 initcode나 이미 배포된 code는 이 제안 전후 모두 실행 시 중단되며 의미가 바뀌지 않습니다. 계정 트라이에 이미 존재하는 0xEF 시작 코드도 그대로 남습니다. 바뀌는 것은 오직 그런 코드를 새로 배포할 수 없게 되는 것뿐입니다.
왜 0xEF인가
0xEF는 Executable Format을 연상시키는 값이라 선택되었습니다. 미할당 명령어를 쓰는 컨트랙트는 의미가 바뀔 위험이 있다고 일반적으로 이해되므로, 미할당인 0xEF를 쓰는 편이 0xFD(REVERT), 0xFE(INVALID), 0xFF(SELFDESTRUCT) 같은 이미 할당된 명령어를 고르는 것보다 영향이 적습니다. 2021년 5월 상태에 있던 18084433개 컨트랙트를 분석한 결과 0xEF로 시작하는 컨트랙트는 0개였고, 0xFD, 0xFE, 0xFF로 시작하는 것은 각각 1개, 4개, 12개였습니다.
테스트 케이스
각 테스트는 세 가지 맥락에서 실행합니다. 계정 코드가 없는 create 트랜잭션, CREATE를 쓰는 계정 코드, CREATE2를 쓰는 계정 코드입니다. 한 바이트 ef, 두 바이트 ef00, 세 바이트 ef0000, 32바이트 ef00...00을 배포하는 경우는 모두 새 컨트랙트가 배포되지 않고 트랜잭션이 실패합니다. 반면 한 바이트 fe를 배포하는 경우는 컨트랙트가 배포되고 트랜잭션이 성공합니다. 즉 거부 기준은 오직 첫 바이트가 0xEF인지이며 길이나 나머지 내용은 보지 않습니다.
호환성: 깨지는 것은 데이터 컨트랙트뿐
0xEF로 시작하는 새 코드가 배포 불가능해지므로 형식상 하위 호환을 깨는 변경입니다. 다만 바이트코드는 첫 바이트부터 실행되므로 0xEF로 시작하는 코드는 어차피 실행할 수 없고 현재 실행 가능한 컨트랙트 중 영향받는 것은 없습니다. 영향이 있는 경우는 코드를 실행하지 않고 데이터 저장용으로 배포하는 컨트랙트가 0xEF로 시작할 때이며 이런 배포는 새로 할 수 없게 됩니다. 저자들은 이 변경이 일으키는 보안 위험이나 서비스 거부(DoS) 위험을 알지 못한다고 밝혔습니다.
EOF가 무산되어도 되돌리기 쉬움
EVM 오브젝트 포맷 제안이 결국 배포되지 않더라도, 예약해 둔 매직은 버저닝에 의존하는 다른 기능이 쓸 수 있습니다. 버저닝 자체가 불필요해지면 0xEF로 시작하는 코드의 배포를 다시 허용하는 것만으로 간단히 되돌릴 수 있습니다. 이 EIP는 Final 상태로 확정되어 이더리움에 적용되었습니다.