EIP-140 REVERT 명령어 (REVERT instruction)

최종 확정 · EVM·실행 · 원문 상태 Final · 2017-02-06 작성

실행을 중단하고 지금까지의 상태 변경을 되돌리는 REVERT 명령어를 EVM에 추가합니다. 남은 가스를 전부 소모하지 않고 실패 이유를 호출자에게 전달할 수 있습니다.

왜 필요한가

이 제안 전에는 스마트 컨트랙트가 실행을 중간에 중단하고 되돌리는 방법이 사실상 두 가지뿐이었습니다. 가스(gas, 거래 실행 수수료)를 일부러 다 써 버리거나, 잘못된 명령어를 실행해 오류를 내는 것입니다. 두 방법 모두 남은 가스를 전부 소모하므로, 조건이 맞지 않아 거래를 안전하게 취소하고 싶을 뿐인 사용자도 수수료를 전부 잃었습니다. 또 실행이 되돌려지면 로그를 포함한 모든 변경이 사라지고 왜 중단됐는지 이유를 전달할 방법도 없었습니다. 사용자는 거래가 실패한 원인을 알 수 없고 지갑이나 앱도 실패 이유를 보여 줄 수 없었습니다. REVERT는 이 두 문제를 한 번에 해결합니다.

무엇이 바뀌나

지금

EIP-140 이후

누구에게 영향이 있나

심층 설명

명령어 정의와 스택 동작

block.number >= BYZANTIUM_FORK_BLKNUM인 블록부터 0xfd 위치에 REVERT 명령어가 도입됩니다. 스택에서 두 항목을 꺼내는데, 맨 위가 memory_offset이고 그다음이 memory_length입니다. 실행을 중단시키므로 스택에 아무것도 올리지 않습니다. memory_offset과 memory_length가 가리키는 바이트열을 원문은 오류 메시지(error message)라고 부릅니다. 메모리 접근과 메모리 비용 계산은 RETURN과 동일합니다.

가스 비용

REVERT의 비용은 RETURN과 같습니다. 롤백 자체는 남은 가스를 소모하지 않고 컨트랙트는 메모리 비용만 지불합니다. 다만 REVERT 비용을 치를 가스가 부족하거나 스택 언더플로가 발생하면, 일반적인 가스 소진(out of gas) 예외와 똑같이 처리되어 남은 가스를 전부 소모합니다.

호출자 쪽 처리

REVERT가 실행되면 실행은 중단되고 실패로 간주되며 상태 변경은 롤백됩니다. 다른 모든 실패와 마찬가지로, 피호출자가 REVERT하면 호출한 쪽의 호출 옵코드는 스택에 0을 반환합니다. 오류 메시지는 호출자의 리턴데이터 버퍼에서 읽을 수 있고 출력 영역에도 복사됩니다. 즉 일반 반환 데이터와 같은 방식으로 다뤄집니다.

CREATE·CREATE2에서의 동작

CREATE나 CREATE2 호출 안에서 REVERT가 실행되면 코드는 배포되지 않고 스택에 0이 올라가며 오류 메시지는 리턴데이터 버퍼에서 읽을 수 있습니다. 컨트랙트 생성 실패도 일반 호출 실패와 같은 방식으로 이유를 전달할 수 있습니다.

오류 메시지 내용은 정의하지 않음

선택적으로 전달하는 메모리 구간의 내용, 즉 오류 메시지의 형식은 이 EIP가 정의하지 않습니다. 원문은 이를 별도의 Informational EIP 후보로 남겨 둡니다. 이 EIP는 오류 코드나 메시지로 해석할 수 있는 바이트열을 전달하는 통로만 제공합니다.

하위 호환성과 테스트 케이스

과거에 생성된 컨트랙트에는 영향이 없습니다. 단, 코드에 0xfd가 명령어로 들어 있는 경우는 예외입니다. 원문 테스트 케이스는 주어진 바이트코드가 0x726576657274206d657373616765를 REVERT 데이터로 반환하고 저장소 키 0x0은 설정되지 않은 채로 남으며 총 20024 가스를 사용해야 한다고 명시합니다. 저장소 쓰기가 롤백되고 메시지가 전달되는지를 함께 검증하는 사례입니다. 이 EIP는 Final 상태로 확정되었습니다.

EIP 원문 · ← EIPs