지난번에 이어 이번에는 Smart Contract 의 응용 예시를 일단 마무리 할 단계이다. 
휴가와 개인사정으로 정말 오랜만의 글이 되었다 ^^;;

▌Crowd Funding 서비스 구성요소

이번 간단한 Crowd Funding 서비스의 컴포넌트 구성은 아래 그림과 같다.


 매우 간단하다. Smart Contract 만을 사용하므로 Server 도 필요 없으며, 사용자 편의를 위한 HTML + JavaScript 기반의 UI 코드 정도만 있으면 된다. 웹서버도 필요 없고 HTML 파일을 Browser 로 열기만 해도 된다.

▪ Browser

 Browser 에서는 새로운 Campaign 을 만들기 위한 간단한 FORM Element 한두개와 투자자가 Contract 로 보낼 투자금을 입력하기 위한 Input 1개, 달성 여부를 점검하기 위한 Text 한줄 정도면 충분하다

▪ web3.js

 Contract 를 Deploy 하고 Contract 내의 함수를 실행하는 부분(Message Transaction, Call)은 web3.js 를 통해서 할 예정이다.
(향후에는 Web App 형태 말고 Go Native App 형태로도 한번 설명해볼 예정이다)
 web3.js 는 내부적으로 RPC 나 IPC 를 사용하여 JSON 형태의 데이터 포맷으로 API 를 호출한다. 이번에는 간단하게 JSON RPC (HTTP) 로 호출하는 기본 동작을 해본다.

▪ Geth (Miner)

 개발환경이므로 Geth 를 띄우고 직접 Mining 을 하도록 구성한다. 다음 글부터 이야기 하겠지만 사실 Geth 대신 TestRPC 를 사용하는게 개발할 때에는 더 편하기도 하다. 여유가 된다면 물론 여러 Miner 나 Node 들을 연결해서 사용하면 된다. 

▪ CrowdFund (Contract)

 Smart Contract Code 이다. Creation Transaction 으로 Blockchain 에 Contract 가 Deploy 된 상태의 그림이다.


: 이렇게 Smart Contract 와 Static Resource 만을 갖고 서버 없는 응용 개발이 가능한게 Smart Contract 의 큰 매력이며, Swarm 이 완성되면 Static Resource 의 배포 방식 또한 완전한 P2P 로 변화 될 것이다. 또한 Whisper 가 완성되면 이러한 응용(Dapp) 간의 Interaction 과 Communication 이 매우 빠르게 다양한 방법으로 가능해지므로 DApp 의 확산이 매우 급속화 될것이라고 믿는다.

▌Crowd Funding 의 동작 흐름

 일단 Smart Contract 를 배포하는 프로세스가 반드시 필요하다. Solidity 로 개발한 Code 를 컴파일하여 Byte Code 와 ABI 를 얻어내고 (이 과정들은 향후 상세히 소개하겠다), 이후 부터는 web3.js 를 응용하거나 RPC API, Go Native Binding 을 사용하여 Smart Contract 의 함수를 실행하게 된다.

▪ UI 소개
 이쁜 UI 에는 소질도 없고, 이해에 집중하기 위한 목적도 추가하여, 최대한 한단한 UI 를 만들어서 소개한다.

 위 HTML Code 를 치는 귀챦음이 두려운 분들을 위해 Gist 에 코드를 올려놓았다.


▪ Contract Creation(Deploy)


 화면의 Create 버튼을 누르면 저장 된 Contract Code (Byte Code) 를 이더리움에 Deploy 한다. 이걸 Contract Creation Transaction 이라고 부르며, 이렇게 Contract 가 정상적으로 Deploy 되면, 그 즉시 ABI 를 통해 생성한 JavaScript 객체를 활용하거나 JSON RPC API 를 통해 Contract 를 사용할 수 있다.

 Contract Code 를 Compile 한 Byte Code 는 HTML 의 JavaScript Code 에 대략 아래 처럼 정의해놓았다.

 코드가 너무 긴 관계로 한줄의 일부만 캡춰 했다. 이렇게 정의해놓은 Byte Code 는 "Create" 버튼을 누르면 아래와 같은 web3.js 를 사용한 JavaScript 코드로 Deploy 된다. (코드와 개발에 대한 설명은 다음에 자세히 할 예정이니 너무 깊게 생각 안해도 된다)

    $('#btnCreate').click(function(){
            crowd = Contract.new({
            data:code,
            gas:2000000,
            from:web3.eth.accounts[0]
        }, function (error, contract) {
            if (!error) {
                if (!contract.address) {
                    _log("Creating Contract : " + contract.transactionHash);
                } else {
                    address = contract.address;
                    _log("Contract Deployed : " + contract.address);
                }
            } else
                console.error(error);
        });
    });

 위에 보면 data:code 부분이 보이는데, 이 부분에 바로 Byte Code 를 넣어서 Transaction 을 발생시키는 것이다.
 Create 를 하면 아래와 같이 화면 상단의 TextArea 에 Contract 를 Deploy 한 Transaction 의 TxHash 가 출력되게 했고, Deploy 가 완료 되었다면 Deploy 된 Contract 의 주소를 출력하도록 했다.


 한번 Deploy 된 Contract 는 Transaction Receipt 를 통해 Contract Address 를 가져올 수 있으며, 이 Contract Address 가 있으면 이후 부터는 이 Address 를 통해 바로 Contract 를 "사용" 하기만 하면 된다. 위 UI 에서는 At 버튼을 만들어서 주소를 써주고 At 을 누르면 ABI 를 통해 Contract 를 사용하기 위한 JavaScript 객체를 만든다.



▪ 신규 캠패인 시작

 신규 캠페인을 시작하기 위해서는 Beneficiery 의 Address 와 목표 금액을 넣기로 했다. 현재 내 Morden-Net 의 Account 는 아래와 같이 두개가 있고, Campaign 의 beneficiery 는 두 개의 계좌 중 두번째 계좌로 지정하여 신규 캠페인을 생성하도록 하겠다. 목표 금액은 100 Ether 로 설정 한다.

 현재 두번째 계좌에는 712 Ether 가 들어있는것을 알 수 있다. 이제 신규 Campaign 을 UI 를 통해 해본다.


 New Campaign 버튼을 누르면 브라우져의 console 로그에 TxHash 를 찍도록 했다.


 그리고 Transaction 이 처리 되었는지 보려면, web3.eth.getTransactionReceipt() 를 사용하는 방법과 etherscan.io 와 같은 Explorer 사이트를 활용하는 방법이 있다.

 만약 Transaction 이 처리되어 Block 으로 묶였다면, 아래와 같이 Transaction 의 Receipt 정보가 보인다.


 혹은 etherscan.io 와 같은 Explorer 사이트에서 확인해보면 아래와 같이 Transaction 이 처리되었음을 알 수 있다.



▪ 목표 달성여부 점검

 특정 캠페인이 목표 투자금액을 달성하였는지를 확인해보도록 한다. 신규 캠페인을 만들거나 투자를 하는 경우와는 달리 달성이 되었는지 확인만 하는 작업은 궂이 Transaction 을 발생시켜 Gas 를 소모시킬 이유가 없다. 이럴 때에는 Local Blockchain 에서 CALL 을 통해 Gas 를 소모하는 Transaction 발생 없이 값을 확인할 수 있다.

 정확히는, Global State 의 변경이 필요한 함수를 실행하는 경우는 SendTransaction 을 통해 Message Transaction 을 발생시켜야 하며, 나의 Local State 만을 임시로 변경하는 테스트 작업을 하거나 State 를 읽어들이기만 하는 작업(Solidity 에서 constant 함수)을 하는 경우에는 CALL 을 사용하도록 하는 것이다.


위와 같이 campaign ID 를 입력하고 check(call) 을 click 하면, 달성 여부와 목표 금액, 현재 모금된 금액이 즉시 표시된다.



▪ 투자 하기

 이번에는 투자를 해보겠다. 나의 1 번 계좌에서 위에서 생성한 0 번 Campaign 에 150 이더를 투자 해 보겠다.

 

 0번 Campaign 에 120 Ether 를 투자하도록 하고, 돈을 지불하는 쪽은 나의 Morden-Net 계좌 중 1번 계좌가 하도록 했다.

 Transaction 이 처리되었는지 위의 "신규 캠페인 시작" 때와 동일한 방법으로 체크를 하고, Transaction 이 처리되었다면 목표 달성 여부를 다시한 번 check 해본다.

 

 120 이더가 0 번 Campaign 에 전달되었다. Contract 의 전체 잔고도 120 Ether 가 된다. 이를 etherscan.io 를 통해 확인해본다.


 위 처럼, Contract 로 120 Ether 가 이체 된 것을 볼 수 있으며, 102,147 Gas 가 소모 되었고, Input Data 는 4바이트의 NewCampaign() 함수의 Signature 인 함수 정의의 SHA-3 해쉬값 중 4바이트가 들어간 것을 볼 수 있다.

 아래는 현재 Contract 의 상태 정보 중 일부이다.



▪ 목표달성 시 펀드 전달

 이제 목표금액이 달성되었다는 사실을 확인 했으니 실제로 달성 금액이 Beneficiery 에게 이체 되도록 해보자. 여기서 한가지 주의해야 할 것이 있다. Smart Contract 는 스스로 함수를 실행할 수 없다는 것이다. 즉, 시스템 시각이 지정한 시각이 되거나 외부 시스템과 주기적으로 인터페이스 하여 조건을 만족하면 함수를 실행하도록 하는 등의 행위는 불가하다.

 Smart Contract 는 반드시
1) 외부의 EOA 에 의해 발생한 Transaction 에 의해 실행되거나
2) 다른 Contract 의 호출에 의해 실행되거나
의 방법으로 호출된다.

 그래서 우리의 Crowd Funding Contract 의 펀드 전달 기능 또한 누군가가 혹은 어떤 시스템이 Smart Contract 를 실행하여 Global State 를 변이하도록 Transaction 을 발생시켜야 한다. 아마도 이 상황에서는 Beneficiery 가 해당 Transaction 을 발생시키는게 합리적이긴 하겠다. 그러나 현재 Contract 는 다른 어떠한 EOA 나 Contract 도 함수를 실행시킬 수 있다.


 UI 의 Check(send) 버튼을 누르면 Check(call) 때와 똑같은 함수를 실행한다 하지만 CALL 이 아니고 Message Transaction 을 발생 시킨다. 그러면 실제 아래 Contract Code 의 c.beneficiary.send(c.amount) 함수가 모든 Node 에서 실행되어 Global State 를 변경하게 된다.

  function checkGoalReached(uint campaignID) returns (bool reached_, uint goal_, uint amount_) {
    Campaign c = campaigns[campaignID];
    goal_ = c.fundingGoal;
    amount_ = c.amount;
    if (c.amount < c.fundingGoal) {
      reached_ = false;
      return;
    }
    c.beneficiary.send(c.amount);
    reached_ = true;
    c.amount = 0;
  }

 Transaction 이 실행되고 난 후 Etherscan.io 를 통해 보면, Contract 가 120 Ether 를 Beneficiery 계좌로 보낸것을 볼 수 있다.


 그리고 다시 0번 캠페인의 상태를 Check 해보면 돈이 빠져나간 것을 확인할 수 있다. 또한 2번째 계좌의 이전 Balance 가 712 Ether 에서 832 Ether 로 변경된 것을 확인할 수 있다.



▌결론

 이번 2회에 걸친 글은 기술적인 글 보다는 "Smart Contract 로 이런게 가능하고, 매우 간단한 예시로는 이런게 있다" 를 전달하기 위한 목적의 글이다. 

 "저장" 뿐만이 아닌 "실행" 가능한 블록체인 기술로, 앞으로 매우 다양한 서비스를 개발하는 사례가 생겨날 것이라고 생각한다. 이러한 사례를 만들어 내기 위한 기술적인 글들을 다음 부터 또 올려보도록 하겠다.


반응형

'Blockchain > Ethereum' 카테고리의 다른 글

Multisig Wallet #2  (497) 2019.01.18
Multisig Wallet #1  (495) 2018.11.30
Ethereum 응용 개발 - Smart Contract 의 응용 예시 (1/2)  (2658) 2016.07.12
Ethereum 응용 개발 - Smart Contract 의 이해  (1924) 2016.07.06
Ethereum 응용 개발 - web3.js 사용하기  (1561) 2016.07.03
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
지난 글에서는 Smart Contract 를 처음 접하는 분들의 이해를 돕기 위한 글을 올렸다. 이번에도 약간 그러한 연장선상에서 Smart Contract 의 응용 예시에 대해 글을 올려보도록 하겠다. 다음 글 정도 부터는 본격적인 개발 이야기로 들어가보도록 하고, 우선 Overview 형태로 Smart Contract 가 돌아서 무엇을 할 수 있는지에 대해 이야기 해보도록 한다.


▌Crowd Funding 의 예시

 Smart Contract 를 이해하기에 가장 좋은 첫 예시는 Crowd Funding 이라고 생각한다. 돈도 들어가고, modifier 를 예로 들기에도 좋고, 코드도 간결하니 사람들이 이해하기에도 쉽고, event 도 넣기 좋고, 좀더 확장하면 DAO 를 이해하기에도 좋고, Contract 간의 Interface 로 확장해 나아가기에도 참 좋은 예시이다.

 일단 Smart Contract 로 가칭 Quick Starter 라는 어디서 많이 들어본것 같은 느낌의 크라우드 펀딩 Contract 가 갖는 기능을 보자.

[Quick Starter]

  ▶ 누구나 캠페인을 만들 수 있다 (캠페인 = 신규 펀딩 유치)
  ▶ 누구나 Ether 를 보내 펀딩할 수 있다
  ▶ 캠페인의 모금액이 목표 모금액보다 크면 수혜자에게 펀드를 전달한다
  ▶ 펀드 전달과 동시에 캠페인은 종료된다
  ▶ [확장] 캠페인 종료와 함께 캠페인 투자자의 지분(토큰)을 기록한다
  ▶ [확장] 수혜자는 매 년 수익을 이더로 공유하면 지분에 따라 수익이 분배된다
  ▶ [확장] 지분 보유자는 Quick Starter 컨트랙트를 통해 지분 거래가 가능하다

대략 이러한 시나리오(계약내용이라고 하지 않으나 시나리오의 구현 내용이 즉 계약 내용이라고 보면 이해가 빠르겠다)를 갖는 Contract 를 조금 도식화 해 보면 아래와 같다.


Smart Contract 에 대해 잘 모르고 비트코인만 접했던 사람들은 두어번 의문을 가지게 된다.

   Contract 에 송금을 한다고?
   지분 부여, 수익 분배, 지분 매도/매수를 Contract 가 한다고?

Bitcoin 의 Transaction 중심의 응용 방법으로는 이해가 가지 않는 부분이며, 동시에 Smart Contract 가 존재하는 가장 큰 이유이기도 하다. 

 비트코인이 처음 나오고 블록체인 기술이 소개될 때 항상 따라다니는 유행어 같은게 있었다. "중개자 없는 P2P 거래" 라는 말이다. 그리고 기업들은 고민한다. "P2P 는 전통적으로 기업이 참여할 시장이 아니야, 거기에다가 중애자 없이라고? 그럼 더 할 이유가 없네!'. 뭐 그런 회사가 있다.. 아니 많이 있다. P2P 가상화폐 이체P2P Digital Asset 발행 및 거래, Transaction 내에 증명데이터(Proof of Existence) 저장 이라는 틀에 갇혀있고 안그래도 복잡한데, 그 이상으로는 이해하고 싶지 않은 노력을 하는 기업들이다. (아 쫌 안티했나..^^;;)

 그러나 위 처럼 Smart Contract 가 끼면서 Blockchain 의 기존 관념에 큰 변화가 생긴다. 바로 "중개자" 가 Blockchain 내에 생기게 되는 것이다. 좀더 정확히 말하자면 "응용 서비스 제공자/가상기업" 이 생기게 된다. 기업이나 개인은 사람들에게 충분한 가치를 제공할 수 있는 서비스라면 Smart Contract 로 그러한 가치를 제공하는 가상기업을 운영하고 수익을 낼 수 있다. 


■ 번외  Smart Contract 현황

 현재 Ethereum 에는 93,711 개의 Smart Contract 가 Deploy 되어있으며, 이러한 Smart Contract Account 에는 총 16,921,390.231 이더가 담겨있다. 한화로 따지면 약 2,115억원 (1 ether 12,500 원 기준) 규모이다. DAO Smart Contract 또한 이 Contract 들 중 하나이며, 이번에 문제가 된 TheDarkDAO 가 이중 21.5% (454억)를 갖고있다. 조만간 무용지물 되겠지만 큰 규모의 악행 이었다. (아 왜 갑자기 이야기가 DAO 로 가나...^^;;)


 위 http://etherscan.io 사이트에서 Contract Account Address 를 클릭하면 Contract 의 Internal Transaction (Contract 간 호출) 과 Contract 의 Byte Code 를 볼 수 있다. 만약, 소스가 공개된 Smart Contract 의 Source Code 까지 보고싶다면, http://etherchain.org 에서 해당 기능을 제공하고있다.

 오늘 카페(http://cafe.naver.com/decentral) 어느분께서 Byte Code 를 Solidity 로 Disassemble 하여 Reverse 가능하냐고 하셨는데, 아직 그런툴은 없다고 답변드렸다. 물론 OPCode 로는 가능하지만 Solidity 로 변환해주는 툴은 아직 없다.

 만약 어느정도 퀄리티가 된다 싶은 공개 Smart Contract 기반의 Dapp 들을 보고 싶다면, http://dapps.ethercasts.com/ 에서 하나씩 눌러보는것들도 재미지다.


 깔끔하게 https://docs.google.com/spreadsheets/d/1VdRMFENPzjL2V-vZhcc_aa5-ysf243t5vXlxC2b054g/edit?usp=sharing 를 통해 엑셀로 정리된 버전을 볼 수도 있다.



▌Smart Contract Code 구조

 이제부터는 Code 로 가보자. 저 위의 Quick Starter 컨셉의 매우 핵심 기능만을 하는 Crowd Funding Contract 를 보자. 지분 개념이나 거래개념은 없으며

  ▶ 누구나 캠페인을 만들 수 있다 (캠페인 = 신규 펀딩 유치)
  ▶ 누구나 Ether 를 보내 펀딩할 수 있다
  ▶ 캠페인의 모금액이 목표 모금액보다 크면 수혜자에게 펀드를 전달한다
  ▶ 펀드 전달과 동시에 캠페인은 종료된다
 
위 기능만을 하는 Contract Code 를 보자. 지금은 그 구조만을 눈여겨 본다. 이 다음 글 부터는 본격적인 Smart Contract 개발환경 설명을 시작으로 개발에 대한 상세한 방법을 써볼 예정이니 일단 참고 보도록 하자. 이후로도 계속 사용할 언어는 Solidity 이다. 작년에는 Serpent 도 섰었지만 이제는 Solidity 가 대세이므로 앞으로도 계속 Solidity 중심으로만 설명한다.

contract CrowdFunding {
  struct Funder {
    address addr;
    uint amount;
  }
  struct Campaign {
    address beneficiary;
    uint fundingGoal;
    uint numFunders;
    uint amount;
    mapping (uint => Funder) funders;
  }
  uint numCampaigns;
  mapping (uint => Campaign) campaigns;
  function newCampaign(address beneficiary, uint goal) returns (uint campaignID){
    campaignID = numCampaigns++
    campaigns[campaignID] = Campaign(beneficiary, goal, 00);
  }
  function contribute(uint campaignID) {
    Campaign c = campaigns[campaignID];
    c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
    c.amount += msg.value;
  }
  function checkGoalReached(uint campaignID) returns (bool reached) {
    Campaign c = campaigns[campaignID];
    if (c.amount < c.fundingGoal)
      return false;
    c.beneficiary.send(c.amount);
    c.amount = 0;
    return true;
  }
}

코드 분석을 위한 글은 아니므로, 코드에 대한 설명을 간단히만 해본다.

contract CrowdFunding {

Contract 를 선언한다. Contract 의 이름은 CrowdFunding 으로 하였다. Library 나 상속, 참조 없이 하나의 Contract 만으로 개발한 코드이므로 1개의 Contract 만 보인다.

 struct Funder {

Funding 을 한 EOA(Externally Owned Account / 사람소유계정)의 정보를 담을 구조체이다. 간단하게 Address 와 투자한 Ether (Wei) 의 양을 저장하고 있다.
Solidity 에서는 이 struct, mapping, dynamic array 를 매우 자주 사용한다. 여러 Account 를 대상으로 하거나 내부적인 데이터를 여럿 갖고있어야 하는 경우가 대부분이므로 이는 매우 중요하다.

  uint numCampaigns;

State 변수를 uint 타입으로 하나 선언한다. uint 는 uint256 과 동일하다. 32바이트 변수이다. 

  mapping (uint => Campaign) campaigns;

앞으로도 매우 자주 보게 될 mapping 변수이다. (괄호) 안의 uint => Campaign 은, Key 를 uint 타입으로 하고, Value 를 Campaign 구조체로 갖는 Mapping 을 만든다는 이야기 이며, 이러한 Map 을 campaigns 라는 이름의 State 변수로 선언한 예이다. Key 는 위의 numCampaigns 의 증가값을 ID 로 사용할 예정이다.

  function newCampaign(address beneficiary, uint goal) returns (uint campaignID){

새로운 Campaign 을 만드는 함수이다. address 타입과 uint 타입으로 파라미터를 받으며, uint 타입을 리턴한다. 수혜자의 EOA Address 와 목표금액을 넣는 함수이다. 그리고 신규 campaign ID 를 리턴하도록 되어있다. 그런데, 이 return 값은 Smart Contract 로 Transaction 을 발생시켜서는 받을 수 없다. 리턴값을 받으려면 CALL 을 해야 한다. 그래서 이 함수는 CALL 로 실행해서 새로운 campaignID 를 받아보는 용도로도 쓰일 수 있으며 실제 Transaction 을 발생해서 신규 캠페인을 등록하는 용도로도 사용할 수 있다. 이 부분에 대해서는 차츰 이해가 가게 될 것이다.

  function contribute(uint campaignID) {

투자자는 이 contribute 함수로 Message Transaction 을 발생시키면서 동시에 투자할 ether 를 Transaction 의 value 로 담아서 보내면 된다. 그러면 해당 campaignID 의 캠페인에 투자를 하게되고, campaigns Map 의 Campaign 내의 funders Map 에 신규 Funder 를 추가하도록 되어있다.

  function checkGoalReached(uint campaignID) returns (bool reached) {

특정 캠페인의 모금액이 목표금액에 도달하였는지를 점검하고, 도달하였다면 실제로 자금을 이체하도록 하는 함수이다. 이 함수 내용을 보면 Smart Contract 의 특징이 하나 보인다. 바로 "Smart Contract 는 스스로 실행될 수 없다" 는 특징이다. 그래서 특정 EOA 나 Contract 가 이 함수에 Message Transaction 을 보냈을 때에만 목표 도달 시 실제 이체를 실행하게 된다. 그러므로 CALL 을 통해 수시로 체크를 하다가 실제 목표 금액에 도달하였을 때 수혜자 혹은 누군가가(수혜자가 해야겠지요?) Message Transaction 을 보내면서 이 함수를 실행시키는것이 비용적으로 타당한 동작 방식이다. 

 짧은 설명이었고, 더 깊고 많은 설명은 점차 기술 설명으로 들어가면서 해 나아가겠다.


▌1/2 끝에

 오늘도 시간이 빨리간다. 사실 오늘 아예 허접하지만 UI 붙이고 돌아가는 흐름과 캡춰 까지 올리려고 하였으나 반만 쓴다.. 졸려서~ ^^;; 

 이제부터는 조금씩 잘라서 여러번 올려야겠다. 코드가 들어가니 괜히 페이지가 길어질 듯 하다.

 다음 글에는 동작 흐름, 실행 결과, 개발로 들어가기 전의 결론에 대해 말해볼 예정이다.


반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
이제부터는 Smart Contract 로 들어가기로 한다.
Ethereum 이 비트코인과 가장 크게 차별화되는 특징이기도 하며, Public/Consortium/Private Blockchain 에 공통으로 적용되어 기존 C/S 환경 대비 가장 큰 변화를 가져다줄 수 있는 기술이기도 하다.

일단 얼마전에, 블록체인의 응용방법을 주제로 한 Blockchain 초급 개발자를 대상으로 생각하여 만든 자료를 사용하여 설명 해보겠다.

▌Smart Contract 의 개요

쉽고 멋들어지게 일반화된 용어로 Smart Contract 를 표현한 뉴스기사나 글들이 참 많다. 그래서일까, 사람들이 Smart Contract (스마트 계약)이라는 용어를 나름 머릿속으로 해석하여 정말 무슨 계약 문서같은것이 블록체인에 담겨있다는 생각을 하는 이들도 많다. 그도 그럴것이, Blockchain 을 응용한 매우 낮은 수준의 응용 방법 중 대표적인 것이 "Proof of Existence" 였고, "진본증명" 이나 "외부거래증명" 같은 Use-Case 를 다수 접해온 Blockchain 초심자들이 상상해내기 딱 좋은 용어의 조합이기 때문이라고 생각한다.

Nick Szabo (닉 싸보)

Smart Contract 의 최초 발안자는 Nick Szabo 이다. 이양반은 Computer Scientist 이며 암호학과 법학에 조회가 깊고 경제학에도 관심이 많은, 보통 사람이 대하기에는 힘겨운 그런 사람이다.

1994년, Smart Contract 라는 개념에 대해 처음으로 발표하게 되고
1997년, The Idea of Smart Contract 라는 글로 실제로 이러한 Smart Contract 를 어디에 적용하면 좋을지에 대한 아이디어를 낸다

▌Smart Contract 키워드

Nick Szabo 가 말하는 Smart Contract 의 목적은, "신뢰할 수 없는 컴퓨터 네트워크환경" 에서 "(Machine 간에)고도로 발달된 자동 계약 이행 방법" 을 제시하는 것이다. 이러한 개념은 블록체인 기술을 만나면서 빛을 발하게 되었고, Ethereum 이 "Turing Complete Blockchain" 이라는 개념으로 Blockchain 을 진화시켜가며 Nick Szabo 의 아이디어를 실현시켜 준다.

Nick Szabo 는 블록체인 내의 Smart Contract 를 아래와 같이 이야기한다.
 
    ▪  코드 조각 이다
    ▪  공유장부와 상호작용할 수 있는 인터페이스
    ▪  Transaction 을 보내면 코드조각의 함수를 실행
    ▪  실행된 함수는 장부에서 값을 읽거나 씀

그리고, 금융을 의식해서인지, 어느 강연에서는 이렇게 이야기한다.


이제 조금 감이 왔을 것이라고 믿는다. 조금만 더 이해를 돕기 위한 이야기를 해보자.

▌Smart Contract 의 정체

Nick 이 말한대로이다. Smart Contract 는 계약가 아니라 코드 조각이다. 

아래는 실제 Solidity 언어로 구현 한 Smart Contract (Code) 이다.

contract CrowdFunding {
  struct Funder {
    address addr;
    uint amount;
  }
  struct Campaign {
    address beneficiary;
    uint fundingGoal;
    uint numFunders;
    uint amount;
    mapping (uint => Funder) funders;
  }
  uint numCampaigns;
  mapping (uint => Campaign) campaigns;
  function newCampaign(address beneficiary, uint goal) returns (uint campaignID){
    campaignID = numCampaigns++; 
    campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
  }
 
...

이러한 Smart Contract Code 는 Ethereum 의 경우, Solidity, Serpent, LLL, Mutan 의 언어로 쓰여질 수 있는데, 현재는 Solidity 를 주로 밀고 있으며 문법은 JavaScript-Like 하다. Serpent 는 Frontier 가 Release 되기 전까지만 해도 C 언어와 유사한 문법이고 Solidity 의 완성도가 많이 떨어져서 두루두루 함께 썼지만 지금은 Solidity 의 승이다. Mutan 은 접은지 꽤 됐고, LLL 은 아직도 Assembly-Like 하게 Low Level 로 개발하고 Debug 하는데에 일부 해외 개발자들이 사용한다.

위에 보이는대로, Smart Contract 는 "변수"도 있고 "구조체"도 있고 "함수"도 있는 코드 이다. 물론 좀더 들어가면 'Storage 변수와 Memory 변수'로 변수의 종류가 나뉘고 Contract 간의 Address 기반 호출과 DELEGATECALL 등이 들어가면서 기존 프로그래밍 방식과 다른 점들이 많이 튀어나오긴 하지만 그래도 코드이다.

이러한 Smart Contract Code 는 Compile 과정을 거쳐 Byte Code 로 변환된다.


위 Bytecode 는 Solidity Realtime Compiler 를 통해 컴파일된 결과이다(도구 등에 대해서도 다음번에 다룰 것이다). 모두 16진수로 된 코드이며, 이 Bytecode 를 to: 주소가 없이 Payload (data: ) 로 할당하여 Blockchain 에 Transaction 을 날리면, Miner 에 의해 Block 이 생성되고, 이러한 Transaction은 Contract Creation Transaction 으로 간주되어 Transaction Receipt 의 contractAddress: 필드에 생성(배포)된 Contract 의 주소를 넣어서 리턴해주게 되어있다.

다음은 이러한 Smart Contract 의 응용 흐름에 대해 간략히 이해 해보자

▌Smart Contract 의 응용 흐름

Smart Contract Code 는 크게 [Creation/Deployment] [Invoke by Message] [Call] 의 응용방식으로 나뉜다.
우선 아래 그림을 보면서 해당 Smart Contract의 응용 흐름을 이해 해보자.


▪   Smart Contract 개발환경
 Smart Contract 개발환경은 개발도구와 Compiler 까지를 포함한 범위를 표시한다. Code 를 작성하고 컴파일 하면 모든 컴파일러는 [Byte Code] 와 [Function Signature], [ABI] 를 최소한 벹어낸다. 

  Byte Code 는 이미 위에서 설명한 것 처럼 Smart Contract Code 를 컴파일 한 결과이며, Blockchain 에 Contract Creation Transaction 을 발생시켜 배포하거나 Contract 로의 Message Tx 이나 Call 을 통해 EVM 위에서 실행된다.

  Function Signature 는 Contract 내의 함수 이름의 SHA3 한 Hash 값의 4바이트 값으로, Contract 의 함수를 실행시킬 때 Transaction 의 to: 주소에는 Contract Address 를, data: 부분에는 이 method signature 4바이트와 함께 파라미터 값이 payload 로 들어간다. JSON RPC API 를 통해 직접 실행시킬때에는 신경써야 하지만 web3.js 를 통해 contract 를 실행할때에는 신경 쓸 필요 없다. 아래 ABI 때문에 가능하다.

  ABI(Application Binary Interface) 는 특정 언어나 플랫폼에 종속되지 않은 방식으로 기술된 Application Interface 에 대한 정의이다. 쉽게 말하면, 이 ABI 정의를 컴파일러 혹은 ABI Generator 가 벹어내는데, 이 ABI 에는 Smart Contract 의 함수와 Parameter 에 대한 Metadata 가 정의되어있다. 이 ABI 를 갖고 JavaScript 언어 기반의 어플리케이션을 만들 때 객체를 만들게 할 수 있고, 쉽게 그 객체의 Method를 호출하는것 만으로 Contract 의 함수가 호출되도록 할 수 있는 것이다. 현재 Ethereum 은 web3.js 와 함께 JavaScript 응용에서 쉽게 ABI 로 객체를 만들어 사용하도록 지원하며, 1.4.0 이후의 go-ethereum 에서는 Go Native 언어 기반의 응용에서 Smart Contract 를 쉽게 Binding 가능하도록 ABI 기반으로 Go Code 를 생성 해주는 ABIGen 을 제공하고있다.

▪   Blockchain Engine
 geth 나 parity, eth 와 같은 Ethereum Node 를 의미한다. 결국 모든 Smart Contract 와 관련한 Transaction 처리와 Contract 실행을 위한 EVM 은 Node 가 갖고있다.

▪   Applications
 Smart Contract 는 Logic 만을 갖고있을 뿐이다. 사용자나 외부 시스템과의 상호작용을 위해서는 당연히 Application 이 필요하다. HTML+CSS+JavaScript 가 되었건 Application Server 가 되었건 Wallet 이건 간에, Ethereum 과의 Interface 를 통해 Smart Contract 와 상호작용하는 Application 에 해당하는 부분이다. Contract 파트를 뺀 Dapp 부분 정도로 봐도 된다.


[1] Contract Creation/Deployment

 일단 bytecode 가 생성되면, ABI 기반의 객체를 통하건 RPC API 를 통하건 Ethereum Network 으로 Contract Creation Transaction 을 날릴 수 있다. 이때 Transaction 에서 to: 주소는 빼고 payload 에 bytecode 를 넣고, 충분한 Gas 를 넣어 보내면(Gas Estimation 기능을 잘 활용해야 한다. 아니면 좀 과하게 넣고 남기면 된다) Contract 가 생성되게 된다.

 ABI 를 통해 생성한 객체의 new() 를 통해 Contract 를 Create 했다면 생성이 완료된 시점(Transaction 이 처리되고 블록으로 묶여서 Import 된 시점)에 callback 이 호출되며 그때 Transaction Receipt 를 보면 contractAddress 에 생성된 Contract 의 주소가 들어가 있게 된다. 이후 부터는 이 주소를 통해 Contract 를 사용하면 되는것이다.

[2] Message Transaction

 위에서 생성한 Contract 의 함수를 실행하는 Transaction 이다. JSON RPC API 로 호출할때에는 payload 에 4바이트의 Function Signature 를 제일 먼저 쓰고 그 다음부터 32바이트 단위의 파라미터 값들을 넣어서 보내게 된다. ABI 를 통한 Contract 객체는 단순히 객체의 Method 를 호출하듯 하면 된다. 

 이 Message Transaction 은 단순히 모든 함수 호출에 사용하면 낭패를 볼 수 있다. Message Transaction 또한 Transaction 이며, Transaction 을 발생시키면 당연히 Gas 가 소모된다. 단순히 현재 상태값을 조회하는 함수를 호출하거나 테스트 목적으로 함수를 호출한다면 Message Transaction 을 발생하면 안된다. 이때는 다음 설명하는 Call 방식을 사용해야 하며, Message Transaction 은, Smart Contract 를 통해 Global State 를 변경해야하는 경우 즉, 값이 변경되어야 하는 경우에만 사용하여야 한다. 나중에 상세히 설명하겠지만, Contract 개발자도 이러한 상태변화가 없는 함수는 constant 로 선언하여야 ABI 로 아무생각없이 함수를 호출하는 응용 사용자들이 Gas 를 소모당하지 않게할 수 있다.

[3] Call

 Contract 의 함수를 호출하는 두번째 방법이다. [2] 에서도 잠깐 언급했지만, Ethereum 의 Global State 에 변화를 주지 않는 함수를 Gas 소모 없이 호출하려면 이 call 을 사용해야 한다. Contract 함수를 Call 하게 되면, Transaction 을 발생시키지 않고 자기 Node 내에 이미 저장되어있는 Smart Contract 를 Local 에서 실행시킨다. constant 함수가 아니더라도 Transaction 발생 없이 함수를 실행시킬 수 있으나 Global State 에는 영향을 주지 않고, call 이 끝난 시점에 모든 state 는 원상복귀된다. 


아.. 오늘은 그냥 "이해" 인데 또 글이 길어졌다.. 더이상 쓰다가는 내일 출근에 지장을 줄 터, 이만 줄이고 앞으로도 많은 내용을 올려야 할 듯 하니 다음 글로 미루어 두도록 하겠다~ ^^;;


▌그래서..

Smart Contract 는, 보이는 대로 블록체인에 배포되는 Code 이다. 그래서 IBM 의 경우, OBC-Peer 를 만들 때 부터 Smart Contract 라는 개념을 가져다 쓰지만 용어는 좀더 Clear 하게, "Chain Code" 라고 부른다. Nick Szabo 에게는 좀 미안하긴 하지만, Contract 라는 표현 보다는 좀더 직관적이지 않나 싶다.

그러나 앞으로의 글들을 보다보면 무조건 Chain Code 라고 하기 보다는 어떤 때는 Contract 라는 용어가 더 맞는것 같다는 느낌이 종종 들 것이다. Public Blockchain 과 Private Blockchain 에서 Smart Contract 가 가져야할 특징과 역할이 약간 다르기 때문인데, 이건 앞으로의 글을 이해하면서 느껴보면 되겠다.

아.. 이젠 졸립다.. 그럼 이만..


반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,