지난번에 이어 이번에는 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 로 이런게 가능하고, 매우 간단한 예시로는 이런게 있다" 를 전달하기 위한 목적의 글이다. 

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


반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
블록체인에 대한 개념과 사토시 나카모토에 대한 이야기를 할 때 빠지지 않는 코스로 잠깐이라도 비잔틴 장군 문제 혹은 비잔티움 장군 문제, 비잔틴 오류 허용 에 대해 이야기가 나오곤 한다. 그런데 대부분 '아 무언가 신뢰할 수 없는 환경에서 어려운 문제를 풀게 해서 신뢰를 얻어내게 했구나' 정도로 이해하면 다행이고, 설명하는 사람들 조차도 어떤 과정으로 그러한 신뢰를 갖게 하였는지에 대해서는 상세히 모르고 설명하는 케이스가 대부분이었다. 

아래는 PoW

▌두 장군의 문제

불확실한 통신 상황에서 상호 동기화를 시도할 때의 문제점으로,
이건 중간의 통신 장애가 있는 경우 어떻게 공격 시간을 전달할것인가라는 문제이며 아직까지 이에 대한 해결방안은 나오지 않았다.

위 처럼 장군 A1, A2 는 가운데 B 에 있는 적들을 공격해야 하는데, 동시 공격하지 않으면 이길 수 없는 상황이다.
A1, A2 사이를 흐르는 강이 있으나, 건너는 경우 연락병이 포획될 가능성이 있다.
이러한 상황에서, A1, A2 는 시간을 합의하고 상호 확답을 받아야 한다. 

이 문제는 비잔틴 장군의 문제와는 다른 문제이다. 


▌비잔틴 장애허용, 비잔틴 장군 문제

300명의 병력이 있는 비잔틴 성을 100명씩의 병력을 가진 장군 5명이 치려함
이기려면 적 병력보다 많은 병력이 공격해야 함
장군들은 연락병을 보내 소통 가능함
장군들 중에는 배신자가 있어 서로 신뢰 불가함

문제) 서로 신뢰할 수 없는데, 어떻게 공격 시각을 합의할 수 있는가? 
     . 가정 1 - 공격 시각은 상관 없음. 모두 한번에 공격할 수 있는 합의된 시각이면 됨
     . 가정 2 - 배신자는 병력을 분산시키려 하므로 이전 장군의 메시지와 다른 시각을 제시함
     . 가정 3 - 각 장군은 자신의 바로 다음 장군에게만 연락할 수 있음
     . 가정 4 - 배신자도 지정한 시간에 공격에 가담 함


<<문제 생기는 상황>>
  1. @장군1 이 "9 AM" 공격시각을 적어 서명과 함께 @장군2 에게 보냄 
  2. @장군2 는 @장군1 의 "9 AM" 공격시각을 보고 기억한 후 @장군3 에게 전달함
  3. @장군3배신자로, "9 AM" 메시지를 찢어버리고 "8 AM" 으로 고쳐서 @장군 4에게 전달함
  4. @장군4 는 "8AM" 을 기억한 후 @장군5 에게 전달
  5. @장군5 는 "8AM" 을 기억
  6. 8AM 에 @장군3, @장군4, @장군5 가 쳐들어가지만 300명이므로 지게 됨

<<PoW 블록체인 해결>>
[새로운 규칙]
A. 장군은 메시지를 보내기 위해 반드시 10분의 시간을 들여야 함
B. 메시지는 모든 이전 장군의 메시지와 10분의 시간을 들였다는 증거를 포함하여 보내야 함

  1. @장군1 이 "9 AM" 공격시각을 적어 10분간 작업하여 증거와 함께 @장군2 에게 보냄
  2. @장군2 는 "9 AM" 메시지와 @장군1 의 10분 작업 증거를 보고 확신 후, @장군3 에게 "9 AM" 메시지 보냄
    1. @장군2 도 10분 간 작업 함
    2. @장군1, @장군2 의 메시지와 작업내용 모두를 포함하여 보냄
  3. @장군3배신자로 "8AM" 으로 메시지를 수정하여 보내고 싶으나 그냥 보낼 수 없음. 아래 중 택 1 해야 함.
    1. 속일 수 있는 유일한 방법
      1. @장군3 은 10분보다 빠르게 작업을 하여 "8 AM" 메시지를 만들어 냄
      2. @장군1, @장군2 의 총 20분 작업에 해당하는 메시지 모두를 남은 시간 내에 만들어 포함 시켜 보냄
    2. 안들키고 있으려면 "9 AM" 으로 보내기
  4. @장군4, @장군5 모두 동일

[의미]
  1. 만약 @장군3 이 "8AM" 으로 보냈다면, 모두 장군 3이 거짓임을 알게 됨
  2. 만약 @장군2 가 배신자였다면 @장군2,3,4,5 모두 9AM 공격 가므로 이기게 됨
  3. 만약 @장군1 이 배신자였다면 8AM 에 모두 함께 공격 가므로 이기게 됨


비잔틴 장군의 문제를 PoW 를 통한 블록체인 기술로 해결 한 예시이다. 블록체인은 "이전 메시지를 포함" 시킨 새로운 메시지로 이해할 수 있으며, 포함된 이전 메지시를 변경한 경우 변경(위조)되었다는 사실을 바로 알 수 있는 장부이다. PoW 는 "10분의 시간을 들여 메시지를 만드는 과정" 이며, 정말 그러한 작업을 하였는지를 단번에 검증해낼 수 있는 방법을 제공한다.
그러나 10분의 시간을 들이지 않고는 절대 새로운 메시지에 포함된 이전 메시지를 다시 만들어낼 수는 없는 알고리즘이며, 비잔틴 장군의 문제 해결에 가장 핵심이 되는 메커니즘인 것이다. 






반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,