지난 글에서는 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, 0, 0);
}
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;
}
}
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);
}
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 붙이고 돌아가는 흐름과 캡춰 까지 올리려고 하였으나 반만 쓴다.. 졸려서~ ^^;;
이제부터는 조금씩 잘라서 여러번 올려야겠다. 코드가 들어가니 괜히 페이지가 길어질 듯 하다.
다음 글에는 동작 흐름, 실행 결과, 개발로 들어가기 전의 결론에 대해 말해볼 예정이다.
반응형
'Blockchain > Ethereum' 카테고리의 다른 글
Multisig Wallet #1 (0) | 2018.11.30 |
---|---|
Ethereum 응용 개발 - Smart Contract 의 응용 예시 (2/2) (2) | 2016.08.15 |
Ethereum 응용 개발 - Smart Contract 의 이해 (3) | 2016.07.06 |
Ethereum 응용 개발 - web3.js 사용하기 (0) | 2016.07.03 |
Ethereum 응용 개발 - JSON RPC 로 Transaction 발생 시키기 (2) | 2016.03.26 |