brunch

매거진 개발실습

You can make anything
by writing

C.S.Lewis

by SKKRYPTO May 06. 2019

[TezosXSkkrypto] 테조스 실습노트 #6

컨트랙트 작성언어 Liquidity

이번 시간에는 테조스의 스마트 컨트랙트를 작성하는데 사용되는 언어인 liquidity에 대해 알아보겠습니다. 이름에서 이더리움의 solidity를 뛰어넘고자 하는 의지를 엿볼 수 있습니다.


 테조스는 미켈슨이라는 로우레벨 정적 유형 언어를 사용하여 스마트 컨트랙트를 작성할 수 있었습니다. 미켈슨을 사용하여 바로 작성하는 것이 가능하였으나, 변수의 유형이 적고 스택 기반의 문법들 때문에 쓰는 것도 읽는 것도 너무 어렵다는 단점이 있었습니다. 그래서 테조스 측은 개발 초기에 좀 더 높은 레벨의 언어가 필요하다는 생각을 하게 되었습니다. 그렇게 해서 OCamlPro 측에서 개발한 리퀴디티의 프로토타입이2017년 6월에 출시를 하게 되었고, 2018년 2월에 정식으로 테조스의 알파넷에서 출시되었습니다.


 리퀴디티는 미켈슨의 타입 시스템을 따르지만, OCaml의 문법을 바탕으로 실행되고 있습니다. 리퀴디티는 미켈슨으로 변환되는 컴파일러가 포함되어 있으며, 미켈슨으로 쓰여진 컨트랙트를 리퀴디티로 변환할 수 있는 디컴파일러 또한 포함되어 있습니다. 이는 서로 다른 언어로 쓰여있어도 편리하게 변환하여 그 내용을 확인하기 위함입니다.


리퀴디티의 정식 웹사이트들은 아래 링크를 통해 확인할 수 있습니다.

-       정식 페이지 : http://www.liquidity-lang.org/

-       온라인 편집기 : http://www.liquidity-lang.org/edit

-       문서 : http://www.liquidity-lang.org/doc

-       깃허브 프로젝트 : http://github.com/OCamlPro/liquidity

-       리퀴디티 튜토리얼 : http://www.liquidity-lang.org/doc/tutorial/tutorial.html

아까 설명했듯이 리퀴디티 프로젝트는 다음과 같은 내용을 포함하고 있습니다.


-       리퀴디티 파일 (.liq 확장자명) 을 미켈슨으로 변환하는 컴파일러

-       미켈슨 파일 (..tz 확장자명) 을 리퀴디티로 변환하는 디컴파일러


이 링크(https://github.com/OCamlPro/liquidity/tree/master/tests)를 통하여 리퀴디티 파일의 예시들을 확인할 수 있습니다.


리퀴디티 설치

먼저 리퀴디티를 설치하는 법을 알아보겠습니다.

먼저 eval $(opam env) 를 통해 opam의 환경변수 설정을 해준 다음에

opam switch create liquidity 4.06.1 를 입력하여 liquidity에 관한 opam switch를 생성해줍니다.


설치가 완료되면 

eval $(opam env)


를 입력하여 현재 셸의 환경을 업데이트 시켜줍니다


터미널에서 앞으로 스위치를 사용할 때에 


eval `opam env --switch liquidity`

를 통해 간편하게 실행해줄 수 있습니다




git clone https://github.com/OCamlPro/liquidity

를 통해 리퀴디티의 깃허브 저장소를 받아주고



cd liquidity

를 통해 해당

 liquidity 폴더로 이동해줍니다.


liquidity 폴더에서 메인넷 브렌치에 있는 테조스의 소스들은 tezos의 하위 디렉토리가 되어야 합니다. 이 것은 간단하게 

make clone-tezos

를 통해 실행할 수 있습니다.


make build-deps

를 통해 리퀴디티 의존성파일들

(dependencies)을 설치합니다.


make

를 통해 빌드를 진행하고

make install

을 통해 설치를 진행해줍니다. 

이 때 꼭 

OPAM switch liquidity 

인 상태에서 커맨드를 입력해야 하는 점 유의하세요!


(cd tests && liquidity test0.liq)

를 통해 간단한 테스트를 해보겠습니다.

커맨드를 입력하면 test0.liq 파일이 test0.tz (미켈슨 파일)로 컴파일된 것을 확인할 수 있습니다.

이상으로 설치과정을 전부 마쳤습니다. 

리퀴디티 기능

이제 간단한 리퀴디티의 사용법을 알아보겠습니다.

리퀴디티를 이용하여 리퀴디티 파일(.liq)을 미켈슨 파일로 컴파일 하는 것과

미켈슨 파일(.tz)을 리퀴디티 파일로 디컴파일하는 과정을 차근차근 진행해보겠습니다.


tests 폴더에 들어가 simple.liq 라는 파일을 생성해보겠습니다.                     


[%%version 0.4]

type storage = int

let%entry main (parameter : int) storage =

( [], storage + parameter )

라는 간단한 파일을 만든 후 liquidity 명령어를 이용해 미켈슨 파일로 컴파일을 진행합니다.


그러면 다음과 같은 내용의 simple.tz 이라는 미켈슨 언어로 짜여진 파일이 생성되는 것을 확인할 수 있습니다. 


만약 liquidity –compact 라는 옵션을 사용한다면 좀 더 압축된 형식의 미켈슨(.tz) 파일을 생성할 수 있습니다.

liquidity –compact simple.liq

를 입력하게 되면 다음과 같이 한 줄로 주어지는

 simple.tz 

파일을 확인할 수 있습니다.


리퀴디티 컨트랙트에 에러가 존재한다면, 컴파일러는 친절하게 에러가 발생한 곳의 위치를 알려줍니다.

simple.liq 의 storage 타입을 int 에서 nat 으로 바꾼 후 컴파일을 진행하면 다음과 같이 에러의 위치를 표시해주는 것을 볼 수 있습니다.


이번엔 반대로 미켈슨 파일(.tz)을 디컴파일하여 리퀴디티 파일을 생성해보겠습니다.


liquidity simple.tz

와 같이 리퀴디티 명령어 뒤에 파일명을 쓰는 것만으로 간단하게 디컴파일이 가능합니다.


이상으로 리퀴디티를 이용한 리퀴디티 파일과 미켈슨 파일의 변환 과정을 알아보았습니다.


리퀴디티 문법

이제 스마트컨트랙트를 작성하기 위해 리퀴디티의 문법을 알아보겠습니다.

먼저, 파이썬과 같은 언어를 통해 널리 알려진 튜플입니다.

리퀴디티의 튜플은 미켈슨에서는 이중 페어로 나타납니다. 따라서 리퀴디티가 미켈슨에 비해 얼마나 편리한 지를 알 수 있습니다


(x,y,z) 

와 같이 튜플의 사용이 가능하며 이는 미켈슨에서 

Pair x (Pair y z)

로 표현이 가능합니다.

튜플의 각 원소는 원소의 위치를 통해 사용이 가능합니다.


let t = (x,y,z) in

let should_be_true = t.(2) = z in 

위 코드에서 볼 수 있듯이 괄호 안의 사용하고자하는 원소의 인덱스를 넣어주면 사용이 가능합니다.


let t = (1,2,3) in

let z = t.(2) <- 4 in 

위 코드는 z를 선언할 때에 t의 인덱스가 2인 원소의 값을 4로 바꾸어 준 튜플을 할당하는 과정입니다. 이 예시와 같이 특정 원소만 값을 수정하여 새로운 튜플을 생성할 수 있습니다.

파이썬과 마찬가지로 튜플의 원소를 각각 분배한 변수 선언 방법도 가능합니다. 

t 가                                        


(int * (bool * nat) * int) * )


의 튜플로 주어질 때에                                           


let _, (b, _), i = t in


를 통해 b와 i 에 원하는 튜플의 원소 값을 넣어줄 수 있습니다. 

다음은 리코드(record) 타입입니다. 

쉽게 생각하면 c언어의 구조체나, 자바의 클래스와 비슷한 개념으로 이해해줄 수 있습니다.


type storage = {

x : string;

y : int;

}


와 같이 구조체 선언과 비슷하게 멤버와 멤버의 자료형을 선언해준 후에


let r = { x = “foo”; y = 3 } in

과 같이 새로운 리코드를 만들어줄 수 있습니다.


r.x

와 같이 생성한 리코드의 멤버를 사용해줄 수 있습니다.

리코드는 튜플로 컴파일 됩니다. 

또한 리코드 안에 리코드가 있는 형태의 선언과 사용도 다음과 같이 가능합니다.   

let r1 = { x = 1; y = { z = 3 } } in

을 통해 선언이 가능하고

let r2 = r1.y.z <- 4 in

 를 통해 값의 수정이 가능합니다.


다음은 변형(Variant) 입니다. 주로 컨트랙트를 작성하기 전, 가장 처음에 사용됩니다. 

c언어의 typedef 을 통해 기존 자료형의 별칭을 정해주었던 것을 생각해주시면 이해가 쉽습니다.

 type t =

| X

| Y of int

| Z of string * nat

다음과 같이 사용할 별칭과 기존의 자료형을 씀으로써 변형 선언이 가능합니다.


let x = X 3 in

let y = Z s in

다음과 같이 변형된 자료형을 컨트랙트 내에서 사용해줄 수 있습니다.

또한 match 를 통해 패턴매칭을 진행할 수 있습니다.


match x with

| X -> …

| Y i -> …

| Z s -> …


다음과 같이 패턴이 일치할 때에 하고자 하는 과정을 화살표 다음 … 부분에 작성해줄 수 있습니다.

이 때 i 와 s 는 변형의 매개변수에 의해 생성된 변수입니다. 



match x with

| X -> …

| Y i -> …

| Z (s, n) -> …


이 매개변수들은 튜플을 분해했던 것과 같이 분해하여 줄 수 있습니다. 


이 예시에서는 s를 (s, n)으로 분해하여 표현하였습니다. 

조금 특별한 변형에 대해 알아보겠습니다.

먼저 Left 와 Right 로 구성되어 있는 variant 입니다.


   type (``left, ``right) variant =

| Left of ``left

| Right of ``right



기존에 구성된 코드는 위와 같습니다.

let x = (Left 3 : (int, string) variant) in

match x with

| Left left  -> ...

| Right right -> ...


variant 를 사용하여준 예입니다. int 가 Left로, string이 Right 로 변형이 되어 사용이 되어지고 있는 모습입니다.


다음은 Source 입니다. 이것은 현재 컨트랙트 자체를 참조하고 있습니다.


let s = (Source : (unit, unit) contract) in


다음과 같이 현재 컨트랙트에 사용되고 있는 unit을 이용할 때에 사용이 가능합니다.


이런 특별한 Left, Right, Source 와 같은 이름들은 다른 것의 별칭으로 지정해줄 수 없는 점 주의해주시기 바랍니다. 

마치 c에서 int 를 변수명으로 사용 불가능한 것과 같습니다.


다음은 함수와 클로저(closure)에 대해 알아보겠습니다.

함수는 다들 익숙한 개념이시겠지만 클로저에 대해서는 처음 보는 분들도 많을 것입니다. 

주로자바스크립트에서 자주 사용이 되는 개념입니다. 클로저의 사전적 정의는 이러합니다.


‘클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 또는, 클로저 안에서 정의된 함수는 만들어진 환경을 기억한다. ‘

간단한 자바스크립트 코드를 통해 좀 더 쉽게 설명을 드리겠습니다.


function outter() {

  let title = ‘fun liquidity’;

  return function() {

    alert(title);

  }

}

inner = outter();

inner();


다음과 같은 코드가 있을 때, outter 함수가 다른 함수를 반환하고 종료됐음에도 불구하고  

outer 함수의 변수인 title을 반환된 함수에서 접근과 사용이 가능합니다. 

사전적 정의에서 보았듯이 만들어진 ‘환경’을 기억하는 이러한 메커니즘을 클로저라고 이해해주시면 되겠습니다.


미켈슨과는 달리 리퀴디티에서 함수는 이러한 클로저 메커니즘을 가질 수 있습니다. 

리퀴디티에서는 여러 인자를 받아들인 후에 하나의 인자로 이루어진 여러 개의 함수로 인식이 가능합니다. 

클로저가 람다 함수 기반이기 때문에 하나의 튜플로 이루어진 인자를 사용하는 것이 좋습니다. 

그러면 인수와 리턴 타입이 유추되기 때문에 여러 개의 인자를 받는 함수를 사용하는 것이 가능해집니다. 

형식에 대한 것을 작성한다면 자료형을 특정적으로 제한할 수 있습니다.

함수는 함수명과 인자를 나란히 놓음으로써 사용할 수 있습니다.


let succ x = x+1 in

let one = succ 0 in



다음과 같이 함수를 정의하고 변수에 대해 사용이 가능합니다. 

이는 |> 라는 Lambda.pipe 라고 불리는 기능을 사용하여 작성할 수 있습니다.


let succ = fun x -> x + 1 in    // 이 부분은 자바스크립트의 화살표 함수와 모양이 비슷합니다.

let one = 0 |> succ in         // 정의된 람다함수에 인자를 넣을 때 |> 를 사용해줍니다.


다른 언어들과 같이 메인 함수 전에 함수를 선언하여 메인함수에서 사용해줄 수 있습니다. 


[%%version 1.01] 


let succ x = x+1 

let%entry main … =

  …

  let one = succ 0 in

  …


메인함수에서 사용한 간단한 예입니다.

앞서 설명한 클로저도 같은 문법으로 사용해줄 수 있습니다.



let p = 10 in

let sum_and_add_p x y = x + y + p in

let r = sum_and_add_p 3 4 in

이라고 작성된 함수는                                           


let p = 10 in

let sum_and_add_p =

  fun x ->

    fun y ->

      x + y+ p

in

let r = (sum_and_add_p 3) 4 in


다음과 같이 위에서 보았던 자바스크립트의 클로저 구조와 비슷하게 

하나의 인자씩을 가지는 람다함수로 써줄 수 있게 됩니다. 

하지만 이렇게 함수를 계속 반환하는 구조는 코드의 길이를 불필요하게 늘리기 때문에, 

여러 개의 인자를 사용한다면 튜플 구조로 인자에 넣어주는 것을 추천드립니다.



let sum_and_add_p (x, y) =

  let p = 10 in

  x + y + p

in

let r = sum_and_add_p (3, 4) in



다음과 같이 튜플을 사용한다면 훨씬 깔끔하게 코드 작성이 가능합니다.

리퀴디티에서는 이렇게 편하게 코드 작성이 가능하지만 

미켈슨에서는 위의 예시 처럼 하나의 인자를 가진 함수들의 반환으로 

계속해서 이루어지기 때문에 훨씬 길고 복잡한 코드가 사용됩니다.


다음은 반복문입니다. 리퀴디티의 반복문은 함수의 문법을 공유합니다.

하지만 반복문 자체는 함수가 아니기 때문에 클로저와 같이 포함되는 함수의 변수 사용이 가능합니다.



let end_loop = 5 in

let x = Loop.loop ( fun x ->

    …

    (x < end_loop, x’)

  ) x_init

in


이 예시에서 볼 수 있듯이 반복문은 반복의 조건과 증가하는 x로 이루어진 페어를 계속해서 반환합니다.


이렇게 해서 리퀴디티의 설치, 리퀴디티의 간단한 기능들, 리퀴디티를 통해 

스마트 컨트랙트를 작성하기 위해 필요한 기본적인 문법들을 알아보았습니다. 

이 마저도 어렵고 생소하다 하시는 분들을 위해 테조스를 사용하는 개발자들이 더욱 편리한 것들을 개발하였습니다.

다음은 파이썬을 이용하여 쉽게 컨트랙트 작성할 수 있는 SmartPy에 대해서 알아볼 수 있는 사이트들입니다.


-       https://medium.com/@SmartPy_io/introducing-smartpy-and-smartpy-io-d4013bee7d4e

-       https://medium.com/tocqueville-group/announcing-smartpy-fe9a62303c0b

-       https://pypi.org/project/smartpy/


다음은 node.js를 이용하여 쉽게 컨트랙트를 작성할 수 있는 Fi에 대해서 알아볼 수 있는 사이트들입니다.


-       https://medium.com/coinmonks/getting-started-with-fi-a8eea75733db

-       https://fi-code.com

-       https://github.com/TezTech/fi-compiler


이렇게 테조스의 스마트 컨트랙트 작성은 정말 다양한 언어로 가능하기 때문에  앞으로 많은 개발자들의 유입이 기대됩니다. 

OCaml 기반의 리퀴디티를 공부해보는 것도 좋은 경험이 될 것입니다. 

자신의 입맛에 맞추어서 편리하게 스마트 컨트랙트를 작성해주시면 되겠습니다.

 긴 글 읽느라 수고하셨습니다. 감사합니다!

매거진의 이전글 [TezosXSkkrypto] 테조스 실습노트 #5
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari