Blockchain

EVM의 새로운 Opcode PUSH0을 소개합니다

EIP-3855

작성자: Choi.eth

5 min read

image.png

Intro

한국 시간 2023년 4월 13일 목요일, 이더리움은 상하이 업그레이드를 통해 실행 레이어(execution layer)에 새로운 표준(EIP)를 반영한다. 이 중에서도 EIP-3855가 추가되어 새로운 Opcode PUSH0를 이더리움 가상 머신(EVM)에 도입한다.

Opcode PUSH0

상하이 업데이트 이후, geth Github의 opcodes.go를 보면 PUSH0(0x5f)가 정의되어 있을것을 볼 수 있다. 기능은 아주 간단하다. EVM Stack 최상단에 0(0x00)을 추가한다.

Bytescode로 컴파일(compile)된 코드에서 Stack 최상단에 0을 추가해야하는 경우는 생각보다 굉장히 빈번하다. 특히, memory나 storage의 offset을 정의할때 0이 필요하다. 예를들어 Opcode MLOAD(offset)는 stack의 최상단(top) 값를 offset 인자로 받아, offset ~ 32bytes 데이터를 memory로 부터 조회한다. 이런 경우는 MLOAD뿐만이 아니다. PCMSIZECALLDATASIZERETURNDATASIZECODESIZECALLVALUESELFBALANCE 등등 굉장히 많은 OPCODE에서 빈번하게 사용된다.

Motivation

EIP-3855가 등장하게된 이유는 스마트 컨트랙트 배포의 가스비를 최적화 하기 위함이다.

PUSH0가 추가되기전, EVM은 PUSH1 00을 통해 Stack 최상단에 0을 넣을 수 있었다. Bytescode 레벨에서 봤을 때 PUSH1 00는 2 length를 차지한다.

https://ethereum.github.io/yellowpaper/paper.pdf#page=27

이더리움의 백서 yellow paper의 27번 페이지를 보면, 스마트 컨트렉트 베포 시, Bytescode 길이(length) 당 200 gas가 측정된다.

We have conducted an analysis on Mainnet (block ranges 8,567,259…8,582,058 and 12,205,970…12,817,405), and ~11.5% of all the PUSH* instructions executed push a value of zero.

메인넷(블록 범위 8,567,259...8,582,058 및 12,205,970...12,817,405)을 분석한 결과, 실행된 모든 PUSH* 명령 중 약 11.5%가 0값을 푸시했습니다.
( 출처 )

EIP-3855에 나와있는 내용을 보면, Mainnet에 배포된 모든 컨트랙트를 합친 코드에서 PUSH1 00는 무려 11.5% 차지했다.

때문에 EIP-3855에는 Bytescode에서 2 length를 차지하는 PUSH1 00대신, 1 length만 차지하는 PUSH0 Opcode로 대체하여 스마트 컨트랙트 배포의 가스비를 최적화하자는 내용이 담겨있다.

Test Cases

EIP-3855를 통해 PUSH0가 생기기전과 생긴 후의 Bytescode length와 컨트랙트 배포 가스비를 Remix를 통해 비교해보자.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.9/contracts/token/ERC20/ERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.9/contracts/token/ERC20/extensions/ERC20Permit.sol";

contract MyToken is ERC20, ERC20Permit {
    constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}
}

contract test {
    function at(address _addr) public view returns (uint256) {
        return _addr.code.length;
    }
}

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-02-10_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.14.09.png

PUSH0Solidity ^0.8.20에 반영이 되었다. OpenZeppelin에서 지원하는 ERC20 코드를 0.8.10와 0.8.20 버전으로 컴파일(compile)했을 때, 결과는 아래와 같다.

solidity ^0.8.10

^0.8.10 deploy erc20 cost

^0.8.10 erc20 bytescode length

solidity ^0.8.20

^0.8.20 deploy erc20 cost

^0.8.20 erc20 bytescode length

Result

deploy cost : 1905383 gas → 1753138 gas

bytescode length : 9279 → 8517

PUSH0가 반영되기 전과 후에 따라 bytescode 길이(length)와 배포 비용이 크게 개선된것을 확인할 수 있다.

Keep reading

모두 보기