새로운 Smart Contract 프로그래밍 언어 만들기 — Compiler (2)

hihiboss
DE-labtory
Published in
6 min readFeb 27, 2019

저번 포스팅에 이어 compiler에 대해 좀 더 자세히 알아보도록 하겠습니다.

Bytecode!

저번 포스팅에서 koa 프로그래밍 언어로 작성된 코드를 vm이 읽을 수 있도록 바꾸는 것이 compiler, 이렇게 바뀐 코드를 bytecode 라고 알아보았습니다. 이번에는 Bytecode에 대해 더 자세하게 알아보도록 하겠습니다!

Bytecode 구조

위 그림은 Bytecode의 구조입니다. 꽤 복잡하죠? 하지만 차근차근 살펴보면 쉽게 이해할 수 있습니다!

Memory Size

VM 컴포넌트는 Compiler가 생성한 Bytecode만을 가지고 프로그램을 실행합니다. 이때, 변수와 Return Value 등을 관리하기 위해 Memory를 사용합니다. (여기에서 Memory에 대해 자세하게 설명하고 있습니다.) 하지만 실제로 Bytecode를 실행하기 전까지 VM은 메모리를 얼마나 사용해야 하는지 알지 못합니다. 그래서 Compiler가 미리 메모리 사용량을 VM에게 전달하여 그 만큼의 메모리를 세팅하도록 하는 것입니다. Memory Size는 이를 위해 존재합니다!

Load Calldata

koa는 일반 프로그램과 조금 다른 성격을 가지고 있습니다. 그 이유는 ‘블록체인'이라는 특성 때문입니다. 일반적인 프로그램은 소스 코드를 컴파일한 후, 컴파일된 프로그램을 실행합니다. 즉, CompileRun 의 과정을 가지는 것이죠. 반면, koa 프로그램은 step이 하나 추가됩니다. 바로 Deploy입니다. 이것은 컴파일된 프로그램을 블록체인 네트워크에 올리는 과정입니다. 쉽게 생각하면, 블록체인 네트워크에 참여하는 모든 노드들에 이 프로그램을 설치하도록 하는 과정이라고 볼 수 있습니다. 따라서, koa 프로그램은 CompileDeployRun 의 과정을 거치게 됩니다.

CalldataDeploy 한 후, 실행하고자 하는 함수 콜을 의미합니다. 이해가 잘 가지 않으니 예시를 통해 알아봅시다!

Calculator라는 프로그램을 koa 언어로 작성하였습니다. 이 프로그램에 Add 함수와 Sub 함수를 작성하였습니다. 코드를 다 작성했으니 먼저 Compile 해야겠죠? Calculator에 대한 Bytecode가 튀어나왔습니다! Bytecode를 블록체인 네트워크에 Deploy 하여 설치했습니다. 옆에 있던 친구가 이렇게 묻네요.

“1+1의 답이 뭐야?”

“음… 우리가 deploy한 Calculator를 사용해보자!”

Add 함수를 사용하면 좋겠죠? 제 블록체인 네트워크에 Add(1,1)을 실행하도록 요청합니다. 이때, Add(1,1)라는 데이터를 Calldata라고 부르는 것입니다.

Load Calldata는 실행하고자 하는 함수와 파라미터에 대한 정보를 불러오는 것입니다!

Function Jumper

앞에서 Load Calldata를 통해 실행하고자 하는 함수의 정보를 불러왔습니다. 그럼 이제 실제로 실행해봐야겠죠? 하지만 하나의 프로그램에는 여러 함수가 구현되어 있는데 내가 실행하고자 하는 함수를 어떻게 찾을까요? 이에 대한 해답이 바로 Function Jumper입니다.

Function Jumper를 살펴보기 전에 잠깐! 먼저, Function Selector에 대해 알아봅시다. Function Selector는 어떤 함수의 고유한 ID를 만드는 방법이라고 생각하시면 됩니다. 함수 정의를 SHA-3 알고리즘으로 Hash 하여 나온 해시값의 앞 4바이트가 바로 그 함수의 ID가 됩니다. (Function Selector여기에서 더 자세하게 설명하고 있습니다.)

이해가 잘 가지 않을 때는 예제를 보는 게 좋겠죠? Add함수를 func Add(int a, int b) int라고 정의해봅시다. int 타입의 변수 a, b를 파라미터로 받아서 int 타입으로 반환하는 함수군요! 그럼 Add의 함수 ID를 구해봅시다. Function SelectorAdd(int,int)라는 값을 넣어 Hash 합니다. bd49b5a77d808bd16e...라는 값이 나왔네요! 이 값의 앞 4바이트는 bd49b5a7군요. 그럼 bd49b5a7Add함수의 ID가 되는 것입니다.

자, 이제 Function Jumper가 어떤 역할을 할지 감이 오시나요? Function Jumper는 실행하고자 하는 함수의 ID를 모든 함수의 ID와 비교하여 실행하려는 함수를 찾고 그곳으로 Jump 시켜주는 역할을 합니다.

Function A, Function B, …

이곳은 프로그램에 정의된 함수들이 Compile 되는 곳입니다! 실제 함수 로직이 담겨있는 곳이죠!

함수의 bytecode 구조

각 함수는 위 그림과 같은 구조로 구성됩니다. 함수를 실행하기 전, 파라미터들을 메모리에 변수로 저장하는 작업을 진행합니다. 그래야 함수 안에서 매개변수를 사용할 수 있겠죠? 그다음엔 함수 코드가 담겨있습니다!

함수들의 bytecode 구조

같은 방법으로 여러 함수가 Compile 되어 위 그림과 같은 구조로 bytecode를 구성하게 되는 것입니다!

Exit

Exit은 프로그램을 종료하기 위한 부분입니다. 실행하고자 하는 함수를 Function Jumper에서 찾을 수 없거나 프로그램이 모두 실행되어 종료될 때 사용됩니다.

지금까지 Bytecode의 구조와 각 부분이 어떤 역할을 수행하는지 알아보았습니다. Memory Size는 VM에게 메모리 사용량을 알리기 위해, Load Calldata는 실행하려는 함수의 정보를 가져오기 위해, Function Jumper는 실행하고자 하는 함수를 찾기 위해, Exit은 프로그램을 종료하기 위해 구성되었습니다. 다음 포스팅에서는 Bytecode에 대해서 코드를 통해 알아보도록 하겠습니다.

--

--