이더리움 스마트 컨트랙트 실습 - 이동현 연구원

강의 기간: 2019.12.24



Remix


Ethereum IDE: http://remix.ethereum.org/ (JSON RPC를 통해 발행)



[Compiler setup]

  1. Solidity version select
    • Metamask 연결 시 Environment - Injected Web3 선택 (네트워크 주소가 바로 아래 표시됨)

  1. Auto compile

  2. File explorers (Solidity 파일들이 있는 곳)

  3. Create new file 클릭 후 코드 작성


  1. Deploy & Run transactions - 컴파일이 되었다면 Solidity compiler에 체크 표시가 되어 있을 것
    • Deploy 클릭으로 네트워크 상에 컨트랙트 발행
    • Deploy contracts 왼쪽 화살표 클릭 후 get에서 데이터가 잘 나오는지 확인
    • set을 다른 값으로 입력하면서 테스트 가능
    • 클립보드 클릭시 해당 컨트랙트 주소 복사 및 확인 가능

  • At address 부분에 주소를 넣으면 해당 주소의 컨트랙트 실행 가능



기본 형태


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pragma solidty >= 0.4.22 < 0.6.0;

contract Contract {
uint256 data; // 변수 생성 (Type 형에 맞게 작성), unsigned int(256bit)

Constructor() public { // Contract가 발행되면서 가장 먼저 실행되는 것 (초기화)
data = 0;
} // → 계약에 담을 내용
function get() public view returns(uint256) { // 여러 개 리턴 가능
return data;
}
function set(uint256 _data) public {
data = _data;
}
}



Solidity 예제 - 계산기 (SafeMath)


  • 연산 과정은 이것을 이용해서 하면 좋다.

  • 덧셈, 뺄셈 등 연산 과정시 underflow, overflow가 발생하면 Library에서 자동으로 찾아 예외처리 한다. (음수 또는 Max 값 초과)

  • using SafeMath for uint


  • 요구 조건 기입: require("조건", "조건 불일치 시 에러 표기 내용")



Solidity 예제 - Ballot


  • 투표에 관련된 기본적인 예제

  • 기본적인 컨트랙트 구성 코드들이 들어있다.


  • 한 개의 투표 당 하나의 컨트랙트를 작성하여 각 선택마다 누적 투표수를 제공하여 의장 역할을 수행하는 작성자가 각 주소에 개별적 투표권을 부여한다. 투표권을 받은 사람들은 투표를 하거나 다른 사람에게 투표권을 위임할 수 있다.



참고: https://solidity-kr.readthedocs.io/ko/latest/solidity-by-example.html



[소스코드 분석]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pragma solidity >=0.4.22 <0.6.0;

contract Ballot {
struct Voter {
uint weight;
bool voted;
uint8 vote;
address delegate;
}
struct Proposal {
uint voteCount;
}

address chairperson; // 컨트랙트를 생성한 사람
mapping(address => Voter) voters; // 각각의 가능한 주소에 대해 Voter 구조체를 저장하는 상태변수 선언
Proposal[] proposals; // 동적 구조체 배열

/// Create a new ballot with $(_numProposals) different proposals.
// Proposal.Names 중 하나를 선택하기 위한 새로운 투표권 생성
constructor(uint8 _numProposals) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
proposals.length = _numProposals; // _numProposals로부터 Proposal의 길이를 입력받음
}

/// Give $(toVoter) the right to vote on this ballot.
/// May only be called by $(chairperson).
function giveRightToVote(address toVoter) public { // 함수를 실행한 사람의 계좌 주소와 chairperson이 다르거나 voter를 지정하는 계좌가 이미 투표를 한 상황이라면 이 함수는 종료, 이전에 반대되는 내용이 조건)
if (msg.sender != chairperson || voters[toVoter].voted) return;
voters[toVoter].weight = 1;
}

/// Delegate your vote to the voter $(to).
// Voter에게 이 투표권에 대한 권한 부여 (Chairperson으로부터 호출 받을 수 있음)
function delegate(address to) public {
Voter storage sender = voters[msg.sender]; // assigns reference
if (sender.voted) return;
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
to = voters[to].delegate;
if (to == msg.sender) return;
sender.voted = true;
sender.delegate = to;
Voter storage delegateTo = voters[to];
if (delegateTo.voted)
proposals[delegateTo.vote].voteCount += sender.weight;
else
delegateTo.weight += sender.weight;
}

/// Give a single vote to proposal $(toProposal).
function vote(uint8 toProposal) public {
Voter storage sender = voters[msg.sender]; // Voters는 매핑 변수. 계좌주소를 가지고 인덱스를 접근하게 해주는 것, sender에 들어가는 사람들은 실제로 투표에 참여하는 사람들을 가리킴
if (sender.voted || toProposal >= proposals.length) return; // 제안 수를 초과하는 경우 함수 실행을 종료시킴
sender.voted = true;
sender.vote = toProposal;
proposals[toProposal].voteCount += sender.weight;
}

function winningProposal() public view returns (uint8 _winningProposal) { // 최종 결과를 출력
uint256 winningVoteCount = 0;
for (uint8 prop = 0; prop < proposals.length; prop++) // 총 Proposal 개수만큼 반복하면서 voteCount보다 클 때 까지 반복하며 최다 투표 값을 확인
if (proposals[prop].voteCount > winningVoteCount) {
winningVoteCount = proposals[prop].voteCount;
_winningProposal = prop;
}
}
}



[코드 관련 자가 테스트]

  1. Voter 구조체와 Proposal 구조체의 쓰임에 대해 설명할 수 있는가?
    • Voter
    • Proposal

  1. 각 구조체는 어떠한 정보를 담기 위함인가? (구조체를 구성하는 각 변수)
    • uint weight
    • bool voted
    • uint8 vote
    • address delegate
    • uint voteCount

  1. 구조체와 구조체의 리스트를 선언하고 사용할 수 있는가?

  2. msg.sender는 무엇을 뜻하는가?

  3. msg.sender와 같은 특성을 갖는 변수를 무엇이라 지칭하는가?

  4. 조건문, 반복문을 사용할 수 있는가?

  5. 생성자 및 함수의 선언을 이해할 수 있는가?


  1. 생성자 및 함수의 동작을 설명할 수 있는가?
    • constructor
    • giveRightToVote(address toVoter)
    • delegate(address to)
    • vote(uint8 toProposal)
    • winningProposal() returns (uint8 winningProposal)

  1. giveRightToVote 함수의 if문을 require문으로 변경할 수 있는가?

  2. 함수 내 storage 변수를 설명할 수 있는가?

  3. Openzeppelin SafeMath 라이브러리를 사용하여 코드를 변경할 수 있는가?

  4. Ballot.sol은 모두 참가자에게 투표권을 부여할 수 있는 형태인가?

  5. 여러 상황에 투표할 수 있는 컨트랙트로 확장할 수 있는 방법은 무엇인가?



Solidity 예제 - Escrow Service (에스크로 서비스)


[스마트 컨트랙트에 필수적으로 구현해야 하는 함수]

  • function registeritem(uint amount) public {…}

  • function buyItem() payable public {…}

  • function refund() public {…} (refund를 제외하고 나머지는 역순으로 갈 수 없다.)

  • function confirmItem() public {…}

  • function sendItem() public {…}



[스마트 컨트랙트 변수]

  • uint public value

  • address payable public seller, buyer

  • string public message

  • enum state {Created, Locked, Sent, Complete} // 상태에 대해서 4가지 값을 가진다.

  • State public state


  • payable: 이더리움과 같이 금액이 들어가는 부분에는 함께 붙는 키워드



[소스코드 분석]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
contract Escrow {
enum State {Created, Locked, Sent, Complete}
uint256 data;
uint256 price;
State state;
address seller;
address buyer;

constructor() public {
data = 0;
}
function buyItem() payable public {
require(state == State.Created, "state is not created");
//require(price == msg.value, "incorrect price");
seller = msg.sender;
state = State.Locked;
}
function get() public view returns(uint256) {
return data;
}
function set(uint256 _data) public {
data = _data;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
pragma solidity >=0.4.22 <0.6.0;

contract EscrowTest {
uint public value;
address payable public seller;
address payable public buyer;
string public message;
enum State {Created, Locked, Sent, Complete}
State public state; // 거래 상태를 나타내고, 흐름에 따라 상태가 계속해서 변화함
mapping(address => uint) balanceOf;

function registerItem(uint amount) public {
seller = msg.sender;
state = State.Created;
value = amount;
}
function buyItem() public payable {
require(msg.value == value, "insert correct price");
buyer = msg.sender;
balanceOf[seller] += value; // 돈이 들어오는 흐름을 확인, 큰 역할은 없음 (여러 명을 상대로 할 경우에는 중요한 역할이 될 수 있음), 매핑 변수
state = State.Locked; // 한 번 실행된 거래에 대해 상태를 변화시킴
}
function refund() public {
require(state == State.Locked, "state is not locked");
balanceOf[seller] -= value;
state = State.Created;
selfdestruct(buyer);
}
function sendItem() public {
require(seller == msg.sender, "You are not seller");
require(state == State.Locked, "state is not locked");
state = State.Sent;
}
function confirmItem() public {
require(buyer == msg.sender, "You are not buyer");
require(state == State.Sent, "state is not sent");
selfdestruct(seller);
state = State.Complete;
}
}



관련 기술 및 기타 전문가 의견


  • 이더리움은 랜덤 함수가 따로 없다.

  • 시드 값은 흘러가는 시간, 문자, 숫자 등을 결합하여 만드는데 랜덤 함수를 만드는 것도 상당히 어렵기 때문에 이를 잘 만들면 블록체인 상에서 신뢰성을 높일 수 있다.

  • 예외처리를 얼마나 잘하느냐에 따라 전문가와 비전문가를 구분한다.



이더리움 스마트 컨트랙트 실습 - 이동현 연구원

https://y8ncastle.world/2020/01/12/education/ethereum/sogang/smart-contract/

Author

Alec J

Posted on

2020-01-12

Updated on

2021-02-09

Licensed under