이더리움 가상머신과 솔리디티를 더 잘 이해하기 위한 지식들
이더리움 가상머신(Ethereum Virtual Machine, 이하 EVM)은 이더리움에서 스마트 컨트랙트를 위한 런타임 환경이다. EVM은 샌드박스-ed 되어 있어 기타 환경과 완벽히 고립된 채로 구동된다는 특징을 가진다. 이 덕분에 EVM에서 구동되는 코드는 다른 프로세스가 접근하지 못하며 불특정 네트워크의 접근도 불가하다.
EVM이 담당하는 역할은 크게 연산과 데이터 저장의 두 부류로 구분할 수 있다.
합의 문제의 발생을 피하고자 EVM에서는 instruction set을 최소한으로 유지한다. 기본적인 대수, 비트, 논리, 비교 연산이 지원되며, 조건부 및 무조건부 jump가 지원된다. 또한 블록체인 자체의 정보를 이용할 수 있도록 블록 넘버와 타임스탬프 등의 정보 접근을 위한 명령어도 지원한다.
하드포크를 통해 각 연산에 소요되는 비용의 변경, 새로운 연산의 추가 및 삭제 등이 이루어질 수 있다.
EVM의 데이터 저장 공간은 일반적으로 스토리지, 메모리, 스택의 세 종류로 구분한다. 이더리움 전체의 관점에서 보면 Calldata나 블록 레벨 영역까지도 포함할 수 있다.
스토리지는 데이터를 영구히 보존하는 영역으로, 256bits의 키(key)로부터 256bits의 벨류(value)에 접근하는 키-벨류 스토어이다. I/O에 하드웨어 비용 및 시간을 많이 사용하기 때문에 스토리지와 관련된 명령어들은 상대적으로 비싼, 더 많은 가스(gas)를 소모하는 특징이 있다. 비용이 많이 들기 때문에 스토리지 사용은 최소화하는 것이 스마트 컨트랙트 개발의 핵심이다. 또한 EVM에서의 스토리지는 읽기/쓰기에 명확한 권한이 필요하다. Public으로 설정되어 있지 않고 internal이나 private으로 설정된 저장값은 다른 컨트랙트가 접근할 수 없다. 대신 Public이나 external 함수로 가시성이 설정된, getter와 setter 역할의 함수들을 통해 접근할 수 있다.
메모리는 각 메시지 콜마다 새롭게 확보되는, 컨트랙트가 사용하는 임시 공간이다. 선형적으로 증가하는데, 당연히 더 많이 확장될수록 더 비싸다. 이때 비용이 Quadratic 수준으로 증가하기 때문에 컨트랙트 개발에 메모리 절감 기법의 고려가 필요하다.
참고로 메시지 콜 역시 Calldata라는 또 하나의 저장 영역을 가지는데, 이를 메모리에 복사하지 않고 그대로 이용하는 등의 기법으로 메모리 증가에 따른 막대한 비용 지불을 방지할 수 있다. 단, 메모리 영역은 수정이 가능하지만 Calldata는 읽기만 가능하다.
스택은 연산을 위한 입력값과 출력값을 다루는 영역이다. EVM은 컴퓨터 하드웨어-like한 레지스터 머신이 아닌, 단순한 구조의 스택 머신이다. 따라서 모든 연산은 스택에서 이루어진다. 최대 스택 사이즈가 1024로 한정되어 있기 때문에, 이를 유의하며 로컬 변수나 파라미터를 적당히 사용해야 한다.
Log를 통해 데이터를 블록 레벨에 저장할 수도 있다. 정의한 이벤트(event)를 emit 하는 것으로 저장할 수 있다. 단, 컨트랙트는 로그가 생기고 난 이후 다시 같은 로그에 접근할 수는 없다. 대신에 블록체인 밖에서 접근하기에 효율적인 영역이다. 이 로그 데이터들은 블룸필터를 통해 또 기록되기 때문에, 암호학적으로 안전한 방법을 통해 전체 블록체인 데이터를 가지고 있지 않은 라이트 클라이언트에서도 로그를 찾을 수 있다.
이외에도 코드 수행 시 위/변조를 방지하기 위해 코드를 업로드하는 가상 ROM 영역, 블록 정보들과 블록체인 자체의 정보를 저장하고 있는 영역 등이 있다.
솔리디티(Solidity)는 이더리움 가상머신을 타겟으로 하는, 스마트 컨트랙트를 구현하기 위한 객체지향 고수준 언어이다. 블록 가스 한도 안에서 수수료만 지불할 수 있다면 모든 종류의 프로그래밍이 가능하다.
솔리디티는 여러 기 존재하는 프로그래밍 언어들의 영향을 많이 받았는데, 특히 초창기에는 자바스크립트의 영향을 많이 받아 설계되었다. 중괄호 언어(Curly-bracket language)라는 점과 import 방식 등에서 그 영향을 살펴볼 수 있다. 심지어 초창기에는 함수 레벨 스코핑이나 var 키워드가 존재하는 등 굉장히 유사했으나, 현재는 보안을 이유로 더 이상 지원하지 않는다. 오히려 0.4버전부터는 변수 선언, 반복문, 오버로딩 등 C/C++에 가까운 모습이라 할 수 있다. 이후 계속된 메이저 버전 업데이트를 통해 편의성과 안전성이 개선되며, 엄격한 타입 변환 등 현재는 나름 독자적인 스타일을 갖추고 있다. 파이썬으로부터도 영향을 받았는데, 데코레이터(솔리디티에서는 모디파이어로 불림)나 다중상속과 super 키워드, 값과 참조 타입의 copy 시멘틱 등이 이에 해당한다.
솔리디티 이외에도 다양한 언어로 EVM 바이트코드를 작성할 수 있다. 바이퍼(Vyper)는 파이썬과 유사한(Pythonic) 컨트랙트 언어이다. Yul/Yul+는 최적화와 보안을 중시할 때 사용하는 중간 언어이자 저수준 언어이다. EVM뿐만 아니라 Ewasm과의 공통분모 작업 그리고 옵티미스틱 롤업 등의 구현에 주로 사용된다. FE는 파이썬과 러스트로부터 영감을 받은, 쉽게 배울 수 있는, 이더리움 에코시스템 입문 개발자용 언어로 개발 중이다.
다양한 언어가 존재하지만 솔리디티를 통한 컨트랙트 개발이 가장 활발한데, 다음과 같은 이유가 있다:
수많은 튜토리얼과 샘플 오픈소스 코드들
개발 및 사용 편의성을 증진시키는 다양한 도구들
큰 개발 커뮤니티
적당한 기능성을 제공함과 동시에, 저수준 개발이 필요한 경우 인라인 어셈블리를 사용할 수 있음
솔리디티의 인라인 어셈블리는 솔리디티의 기능성을 넘어선 저수준의 개발을 편리하게 하는 기능이다. 어셈블리임에도 불구하고 Functional 스타일을 제공해서 쉬운 개발이 가능하다.
가령 다음과 같은 저수준 코드를
다음과 같이 코딩할 수 있다.
동일한 의미를 가지고 있으나 Functional 스타일을 제공하는 덕분에 가독성과 개발 편의성이 증대된다.
인라인 어셈블리를 통해 직접 최적화된 코드를 작성할 수 있지만, 역으로 컴파일러가 수행하는 최적화의 혜택을 받지 못할 수 있다. 따라서 확실하게 최적화할 수 있는 부분으로 판단될 경우에만 인라인 어셈블리로 작성하고 나머지는 컴파일러에게 맡기는 것이 효율적이다.
이더리움을 포크(fork)한 여러 프로젝트들도 대부분 EVM을 그대로 사용하거나 호환성을 제공하고 있다. 그 덕분에 솔리디티를 사용한 컨트랙트 개발이 가능하며, 이미 존재하는 여러 도구 및 서비스에 자연스러운 연결이 가능하다. 가령 디버깅, 편리한 컨트랙트 배포 및 실행, 그리고 여러 확장프로그램을 지원하는 웹 IDE인 REMIX를 활용할 수 있다.
다만 이더리움의 EVM 명령어 업데이트와 솔리디티 버전업은 꾸준히 이루어지고 있기 때문에, 체인 포크 이후에도 지속해서 해당 업그레이드 반영을 해주는 것이 건강한 프로젝트라 할 수 있겠다.
예를 들어, 메타디움의 경우 2022년 9월 현재 master 브랜치와 최신 릴리즈 버전을 기준으로 이더리움의 이스탄불 하드포크 내용까지 적용되어 있다.
이스탄불은 CHAINID와 EXTCODEHASH opcode가 추가되는 등 EVM 레벨에서의 변화가 있었던 하드포크이다. 실제로 메타디움에 이 두 opcode가 추가되어 있음을 코드로 확인할 수가 있다.
또한 이스탄불에서는 기존 opcode의 가스 비용도 여럿 변경되었다. 가령 SLOAD의 가스 비용이 200에서 800으로 변경되었는데, 메타디움에서도 관련 코드들을 살펴볼 수 있다.
이스탄불 이후에도 크고 작은 변경사항들이 이더리움에 많이 있었는데, 이들의 반영사항은 메타디움 개발 브랜치에서 확인할 수가 있었다. 현재를 기준으로 런던 하드포크까지 적용되어 있으며, 그 유명한 가스비 메커니즘 변경인 EIP-1559도 적용되어 있다.
아직은 개발 브랜치에 존재하긴 하지만, 메타디움에서는 이더리움의 업데이트를 추적하여 지속해서 반영해주고 있음을 알 수 있다.
Writer : Luke Park
https://ethereum.org/en/developers/docs/
https://docs.soliditylang.org/en/v0.8.16/
https://remix-ide.readthedocs.io/en/latest/