새로운 Smart Contract 프로그래밍 언어 만들기 — Compiler (2)
저번 포스팅에 이어 compiler에 대해 좀 더 자세히 알아보도록 하겠습니다.
Bytecode!
저번 포스팅에서 koa 프로그래밍 언어로 작성된 코드를 vm이 읽을 수 있도록 바꾸는 것이 compiler, 이렇게 바뀐 코드를 bytecode 라고 알아보았습니다. 이번에는 Bytecode에 대해 더 자세하게 알아보도록 하겠습니다!
위 그림은 Bytecode의 구조입니다. 꽤 복잡하죠? 하지만 차근차근 살펴보면 쉽게 이해할 수 있습니다!
Memory Size
VM 컴포넌트는 Compiler가 생성한 Bytecode만을 가지고 프로그램을 실행합니다. 이때, 변수와 Return Value 등을 관리하기 위해 Memory를 사용합니다. (여기에서 Memory에 대해 자세하게 설명하고 있습니다.) 하지만 실제로 Bytecode를 실행하기 전까지 VM은 메모리를 얼마나 사용해야 하는지 알지 못합니다. 그래서 Compiler가 미리 메모리 사용량을 VM에게 전달하여 그 만큼의 메모리를 세팅하도록 하는 것입니다. Memory Size는 이를 위해 존재합니다!
Load Calldata
koa는 일반 프로그램과 조금 다른 성격을 가지고 있습니다. 그 이유는 ‘블록체인'이라는 특성 때문입니다. 일반적인 프로그램은 소스 코드를 컴파일한 후, 컴파일된 프로그램을 실행합니다. 즉, Compile
— Run
의 과정을 가지는 것이죠. 반면, koa 프로그램은 step이 하나 추가됩니다. 바로 Deploy
입니다. 이것은 컴파일된 프로그램을 블록체인 네트워크에 올리는 과정입니다. 쉽게 생각하면, 블록체인 네트워크에 참여하는 모든 노드들에 이 프로그램을 설치하도록 하는 과정이라고 볼 수 있습니다. 따라서, koa 프로그램은 Compile
— Deploy
— Run
의 과정을 거치게 됩니다.
Calldata
는 Deploy
한 후, 실행하고자 하는 함수 콜을 의미합니다. 이해가 잘 가지 않으니 예시를 통해 알아봅시다!
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 Selector
에 Add(int,int)
라는 값을 넣어 Hash 합니다. bd49b5a77d808bd16e...
라는 값이 나왔네요! 이 값의 앞 4바이트는 bd49b5a7
군요. 그럼 bd49b5a7
이 Add
함수의 ID가 되는 것입니다.
자, 이제 Function Jumper
가 어떤 역할을 할지 감이 오시나요? Function Jumper
는 실행하고자 하는 함수의 ID를 모든 함수의 ID와 비교하여 실행하려는 함수를 찾고 그곳으로 Jump 시켜주는 역할을 합니다.
Function A, Function B, …
이곳은 프로그램에 정의된 함수들이 Compile
되는 곳입니다! 실제 함수 로직이 담겨있는 곳이죠!
각 함수는 위 그림과 같은 구조로 구성됩니다. 함수를 실행하기 전, 파라미터들을 메모리에 변수로 저장하는 작업을 진행합니다. 그래야 함수 안에서 매개변수를 사용할 수 있겠죠? 그다음엔 함수 코드가 담겨있습니다!
같은 방법으로 여러 함수가 Compile
되어 위 그림과 같은 구조로 bytecode를 구성하게 되는 것입니다!
Exit
Exit
은 프로그램을 종료하기 위한 부분입니다. 실행하고자 하는 함수를 Function Jumper
에서 찾을 수 없거나 프로그램이 모두 실행되어 종료될 때 사용됩니다.
지금까지 Bytecode의 구조와 각 부분이 어떤 역할을 수행하는지 알아보았습니다. Memory Size
는 VM에게 메모리 사용량을 알리기 위해, Load Calldata
는 실행하려는 함수의 정보를 가져오기 위해, Function Jumper
는 실행하고자 하는 함수를 찾기 위해, Exit
은 프로그램을 종료하기 위해 구성되었습니다. 다음 포스팅에서는 Bytecode에 대해서 코드를 통해 알아보도록 하겠습니다.