테조스 스마트 컨트랙트 예제 (feat. Fi)
이번에는 테조스 스마트 컨트랙트를 작성할 수 있는 또다른 언어 Fi (발음은 fee로 한다고 합니다)를 공부하고 Fi로 작성한 스마트 컨트랙트를 테조스 블록체인 위에 배포하는 과정까지 보여주려고 합니다.
지난 포스팅에서 공부했던 Liquidity는 미켈슨과 같은 함수형 프로그래밍 방식을 사용하면서 더 작성하기 편하도록 개발된 high level language였습니다. 그러나 기존에 널리 쓰이는 프로그래밍 언어들에 비해 낯선 문법 형태이기 때문에 liquidity보다 더 접근성이 높은 언어의 필요성에서 개발된 것이 바로 “Fi” 입니다.
Fi는 Javascript의 문법을 따르면서 정적 타입 선언을 사용하는 특징을 가지고 또 동시에 Michelson으로 컴파일 가능한 high-level language로, eztz(개발팀 4편!)를 개발했던 TezTech 팀에서 지난 2월 말에 개발 완료하였습니다.
그러면 실제 스마트 컨트랙트의 코드를 살펴보기 이전에 컨트랙트 코드를 이해하는 데 필수적인 Fi 문법만 간단하게 설명하고 넘어가겠습니다. 보다 자세하게 Fi 문법을 알고 싶다면 다음 사이트에서 공부할 수 있습니다.
스마트 컨트랙트는 크게 두 부분으로 구성됩니다.
: 컨트랙트의 상태를 저장하는 Storage & 유저와 상호작용할 수 있는 Entry Point
Storage
storage <type> <variable name>;
위와 같이 선언해서 이 변수가 컨트랙트의 storage에 저장되는 변수임을 명시해줍니다. storage에 저장된 변수에 접근할 때는 예문처럼 storage.<variable name> 으로 불러와서 사용합니다.
(이 문법이 딱히 어려울 것은 없지만 다른 언어와 비교했을 때 굳이 한 단계가 더 필요한 느낌인데요. 제 생각에는 미켈슨의 storage로 컴파일하는 과정을 쉽게 만들고 싶었던 것 같습니다.)
storage 변수 이외에도 이외에 constant나 local 변수, 구조체 등을 선언할 수 있습니다.
Entry point
일반적인 함수 선언 및 정의와 비슷합니다. entry로 선언하고 함수의 이름과 매개변수의 타입을 명시합니다.
Global Constants
사용자가 직접 정의해서 사용하는 Local variable 이외에 테조스 블록체인에서 정보를 받아와 사용할 수 있는 Global constants입니다.
이를 참고해서 다음의 스마트 컨트랙트 코드 전문을 살펴보겠습니다. 컨트랙트 매니저가 설정한 투표 항목에 대해서 투표 권한을 부여받은 사람들이 투표를 진행할 수 있는 간단한 샘플 컨트랙트입니다.
이제 이것을 가지고 storage와 entry point 두 가지로 나눠서 구조를 살펴보겠습니다.
Storages
1. storage map[address=>bool] voter;
: 투표권한이 있는 사람의 address와 그 사람의 기투표 여부를 bool로 맵핑하여 저장합니다.
2. storage map[int=>Proposal] ballot;
: key값을 int로 해서 투표항목에 번호를 매기고 투표 항목의 구체적인 내용(description: 항목의 내용, votes: 받은 투표 수)을 담고 있는 Proposal 구조체를 맵핑하여 저장합니다.
3. storage address manager;
: 컨트랙트를 관리하는 권한을 가진 매니저를 address로 저장합니다.
4. storage string info;
: 컨트랙트에 대한 설명을 string으로 저장합니다.
Entry points
1. entry addAuthorizedVoter(address addr);
: 투표권을 부여하는 함수입니다. SENDER가 매니저인지 확인한 후에 인자로 넘겨받은 address를 voter에 저장하고 기투표 여부는 false로 초기화합니다.
2. entry addProposal(int id, Proposal proposal);
: 투표항목을 추가하는 함수입니다. 마찬가지로 SENDER가 매니저인지 확인한 후에 ballot에 id와 proposal을 맵핑하여 저장합니다.
3. entry addInfo(string info);
: 본 컨트랙트에 대한 설명을 등록하는 함수입니다. 동일하게 매니저만 접근할 수 있습니다.
4. entry placeVote(int vote);
: 투표자가 투표하는 함수입니다. in(a, b)은 map 변수 a 안에 b로 된 key 값이 존재하는지 확인해서 결과를 true/false로 반환하는 함수입니다. 먼저 SENDER가 투표권이 있는지 확인하고, input으로 받은 번호가 존재하는 투표 항목인지 확인합니다. 그 다음, 투표자의 기투표 여부를 확인해서 이전에 투표한 적이 없다면 이를 true로 바꿔주고 투표한 번호에 해당하는 Proposal의 투표수(votes)를 +1 해줍니다.
이렇게 작성한 컨트랙트 코드를 voting.fi라는 이름의 파일로 저장하겠습니다. 이제 이 fi 파일을 컴파일해줄 fi-compiler를 설치해야 하는데요. fi compiler 설치를 위해서는 Node.js가 먼저 설치되어 있어야 합니다. 설치가 되어있지 않다면 다음을 차례대로 입력해서 Node.js를 먼저 설치해 줍니다.
$ sudo apt-get install curl software-properties-common
$ curl -sL https://deb.nodesource.com/setup_12.x | sudo bash -
$ sudo apt-get install nodejs
여기까지 하고나서 node -v와 npm -v 명령어로 각각의 버전이 잘 확인된다면 설치 완료입니다.
$ git clone https://github.com/TezTech/fi-compiler.git
$ cd fi-compiler
$ sudo npm i -g fi-cli
여기까지 하고 나면 fi-compiler까지 모두 설치되었습니다. 이제 Tezos 폴더로 돌아와서 위에서 저장했던 voting.fi 파일을 컴파일해주겠습니다.
$ sudo fi compile voting.fi
컴파일하고 나면 위와 같이 abi 파일과 ml 파일이 생성됩니다.
ml 파일을 열어보면 우리 컨트랙트의 storage가 Michelson으로 어떻게 컴파일되어있는지 확인할 수 있습니다. (storage 변수가 선언된 순서대로 비교해보면 쉽게 이해할 수 있습니다.) 이걸 참고해서 컨트랙트 배포할 때 storage를 초기화할 수 있습니다(안해도 됩니다 init 생략 가능). contract를 originate하는 명령어는 다음과 같습니다.
$ ./tezos-client originate contract <컨트랙트 이름> for <내 지갑 이름(컨트랙트 오너)> transferring <컨트랙트에 송금할 테지의 양> from <내 지갑 이름(출금될 지갑)> running <배포할 컨트랙트 코드 파일> --init <storage 초깃값 설정> --burn-cap <필요한 burn fee>
다음은 제가 실제로 배포할 때 사용한 명령어입니다.
$ ./tezos-client originate contract voting for yessir transferring 0 from yessir running ./voting.fi.ml --init 'Pair {Elt "tz1TDDE3gYvnYxUTsxBPY9SQKdkUqkqqo7N7" False} (Pair {Elt 1 (Pair "yeseo" 0)} (Pair "tz1TDDE3gYvnYxUTsxBPY9SQKdkUqkqqo7N7" "Voting for Yeseo"))' --burn-cap 3.244
**init 부분에서 “tz1TDD~” 주소는 제 지갑 yessir의 주소입니다.
그러면 이렇게 트랜잭션에 대한 정보와 함께 contract origination 결과가 출력이 됩니다.
위 사진의 아랫 부분에서 Balance updates 부분을 보시면 제 지갑 주소인 “tz1TDD~”에서 0.00126 테지가 빠져나간 것을 확인할 수 있습니다. 즉, 컨트랙트에 돈을 송금하지 않더라도 이 트랜잭션 fee를 지불할 잔액이 계좌에 남아있어야 deploy를 성공적으로 실행시킬 수 있습니다.
위 사진에서 Balance updates를 보면 제 계좌에서 또 -2.939 테지가 빠져나갔습니다. (왜케 비싸죠..?) 이것은 제가 –-init 명령어를 사용해서 storage를 변경시킨 것에 대한 gas fee입니다.
중간에 보시면 New contract KT1V7~ 하고 제가 배포한 컨트랙트의 주소가 나와있습니다.
https://alphanet.tzscan.io/ 이 사이트에서 이 주소를 검색하면
이렇게 테조스 알파넷 위에 잘 올라가 있는 우리의 스마트 컨트랙트를 확인할 수 있습니다.
이렇게 하면 터미널에서도 컨트랙트 매니저의 주소를 불러올 수 있습니다. aka yessir라는 사실도 친절하게 알려주네요.
사실 이 모든 컴파일과 디플로이 과정이 어렵거나 혹은 귀찮은 분들을 위해서 https://fi-code.com 에서 이를 지원하고 있습니다.
작성자 : 김예서
개발팀 : 김예서 김승태 김형준