EVM은 이더리움 블록체인의 스마트 계약을 실행하는 분산 컴퓨팅 환경입니다. EVM은 분산된 상태에서도 동일한 결과를 보장하기 위해 설계되었습니다. 모든 이더리움 노드는 EVM을 실행하며, EVM은 스마트 계약을 바이트코드로 실행합니다.
바이트코드는 고급 프로그래밍 언어(예: 솔리디티)로 작성된 스마트 계약이 컴파일된 후 생성되는 저수준 코드입니다. 이 바이트코드는 EVM이 이해하고 실행할 수 있는 명령어(opcode)로 구성됩니다.
EVM이 바이트 코드를 처리하는 방식은 CPU가 명령어를 처리하는 과정과 비슷하지만 EVM은 명령어(opcode)들을 순차적으로 실행하면서 파라미터나 연산의 결과 값을 스택(메모리)에 저장하면서 명령어들을 실행한다는 차이점이 있습니다.
바이트코드는 연속된 명령어 집합으로, 각 명령어는 특정한 작업을 수행합니다. 예를 들어, PUSH, POP, ADD 등의 명령어가 있습니다. 이 명령어들은 EVM 스택에서 작동합니다.
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 storedData;
function set(uint256 x) public {
storedData = x;
}
function get() public view returns (uint256) {
return storedData;
}
}
위 예시코드를 EVM이 이해할 수 있는 바이트 코드로 컴파일하면 아래와 같은 바이트 코드가 생성됩니다.
608060405234801561001057600080fd5b50610133806100206000396000f3fe60806040526004361061003f5760003560e01c806360fe47b1146100445780636d4ce63c1461006e575b600080fd5b61004c61008a565b6040516100599190610101565b60405180910390f35b6100766100aa565b6040516100839190610101565b60405180910390f35b60008054905090565b600055565b6000548156fea2646970667358221220d1c4a65b9d24b8a6b8e173d15de24b5d4d5bfe82e69ad738f81a02ab8e764e0064736f6c63430008040033
그리고 위에서 얻은 바이트 코드를 아래의 파이썬 코드(바이트 코드를 분리해서 opcode를 추출하는 코드)에 넣어주면 다음과 같은 opcode가 추출되게 됩니다.
hashes = set()
for i in range(len(opcodes) - 3):
if (
opcodes[i].name == "PUSH4"
and opcodes[i + 1].name == "EQ"
and opcodes[i + 2].name == "PUSH2"
and opcodes[i + 3].name == "JUMPI"
):
hashes.add(opcodes[i].operand)
hashes = list(hashes)
['6d4ce63c', '60fe47b1']
이 두개의 opcode는 위 컨트랙트에 정의되어 있는 두개의 함수일 것이며 이 opcode를 target.call을 통해서 함수를 호출할 때 function selector(4 bytes)로도 사용 가능합니다.
함수 셀렉터란?
함수 셀렉터는 솔리디티에서 함수 호출을 구별하기 위해 사용되는 고유한 식별자입니다. 함수 셀렉터는 함수의 시그니처(signature)를 해시 함수 keccak256에 적용한 후 처음 4바이트를 취하여 생성됩니다.
위에서 추출한 옵코드 이외에는 스택에 데이터를 저장, 추출, 다음 목적지 지정 등 다른 많은 연산들이 바이트 코드를 이루게 됩니다.
이 명령어들은 스마트 컨트랙트에서도 인라인 어셈블리를 통해서 저수준에서 직접 호출 가능합니다.
이와 같이 바이트코드는 EVM이 스마트 계약을 실행하는 데 있어 핵심적인 역할을 하며, 스마트 계약의 기능을 이더리움 네트워크에서 실현시킵니다.